diff --git a/ea-scripts/OCR - Optical Character Recognition.md b/ea-scripts/Archive/OCR - Optical Character Recognition.md similarity index 96% rename from ea-scripts/OCR - Optical Character Recognition.md rename to ea-scripts/Archive/OCR - Optical Character Recognition.md index 1ad4cd9..0eef5fe 100644 --- a/ea-scripts/OCR - Optical Character Recognition.md +++ b/ea-scripts/Archive/OCR - Optical Character Recognition.md @@ -1,120 +1,120 @@ -/* -![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-ocr.jpg) - -THIS SCRIPT REQUIRES EXCALIDRAW 1.5.15 - -The script will - 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and - 2) will add the text to your drawing as a text element - -I recommend also installing the [Transfer TextElements to Excalidraw markdown metadata](Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md) script as well. - -The script is based on [@schlundd](https://github.com/schlundd)'s [Obsidian-OCR-Plugin](https://github.com/schlundd/obsidian-ocr-plugin) - -See ScriptEngine documentation for more details: -https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html - -```javascript -*/ -if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.24")) { - new Notice("This script requires a newer version of Excalidraw. Please install the latest version."); - return; -} - -let token = ea.getScriptSettings().token?.value??ea.getScriptSettings().token; -const BASE_URL = "https://ocr.taskbone.com"; - -//convert setting to 1.5.21 format -if(token && !ea.getScriptSettings().token.value) { - ea.setScriptSettings({token: {value: token, hidden: true}}); -} - -//get new token if token was not provided -if (!token) { - const tokenResponse = await fetch( - BASE_URL + "/get-new-token", { - method: 'post' - }); - if (tokenResponse.status === 200) { - jsonResponse = await tokenResponse.json(); - token = jsonResponse.token; - ea.setScriptSettings({token: {value: token, hidden: true}}); - } else { - notice(`Taskbone OCR Error: ${tokenResponse.status}\nPlease try again later.`); - return; - } -} - -//get image element -//if multiple image elements were selected prompt user to choose -const imageElements = ea.getViewSelectedElements().filter((el)=>el.type==="image"); - -//need to save the view to ensure recently pasted images are saved as files -await ea.targetView.save(); - -let selectedImageElement = null; -switch (imageElements.length) { - case 0: - return; - case 1: - selectedImageElement = imageElements[0]; - break; - default: - const files = imageElements.map((el)=>ea.getViewFileForImageElement(el)); - selectedImageElement = await utils.suggester(files.map((f)=>f.name),imageElements); - break; -} - -if(!selectedImageElement) { - notice("No image element was selected"); - return; -} -const imageFile = ea.getViewFileForImageElement(selectedImageElement); -if(!imageFile) { - notice("Can read image file"); - return; -} - -//Execute the OCR -let text = null; -const fileBuffer = await app.vault.readBinary(imageFile); -const formData = new FormData(); -formData.append("image", new Blob([fileBuffer])) -try { - const response = await fetch( - BASE_URL + "/get-text", { - headers: { - Authorization: "Bearer " + token - }, - method: "post", - body: formData - }); - if (response.status == 200) { - jsonResponse = await response.json(); - text = jsonResponse?.text; - } else { - notice(`Could not read Text from ${file.path}:\n Error: ${response.status}`); - return; - } -} catch (error) { - notice(`The OCR service seems unavailable right now. Please try again later.`); - return; -} - -if(!text) { - notice("No text found"); - return; -} -console.log({text}); - -//add text element to drawing -const id = ea.addText(selectedImageElement.x,selectedImageElement.y+selectedImageElement.height,text); -await ea.addElementsToView(); -ea.selectElementsInView([ea.getElement(id)]); -ea.getExcalidrawAPI().zoomToFit(ea.getViewSelectedElements(),1); - -//utility function -function notice(message) { - new Notice(message,10000); - console.log(message); -} +/* +![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-ocr.jpg) + +THIS SCRIPT REQUIRES EXCALIDRAW 1.5.15 + +The script will + 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and + 2) will add the text to your drawing as a text element + +I recommend also installing the [Transfer TextElements to Excalidraw markdown metadata](Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md) script as well. + +The script is based on [@schlundd](https://github.com/schlundd)'s [Obsidian-OCR-Plugin](https://github.com/schlundd/obsidian-ocr-plugin) + +See ScriptEngine documentation for more details: +https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html + +```javascript +*/ +if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.24")) { + new Notice("This script requires a newer version of Excalidraw. Please install the latest version."); + return; +} + +let token = ea.getScriptSettings().token?.value??ea.getScriptSettings().token; +const BASE_URL = "https://ocr.taskbone.com"; + +//convert setting to 1.5.21 format +if(token && !ea.getScriptSettings().token.value) { + ea.setScriptSettings({token: {value: token, hidden: true}}); +} + +//get new token if token was not provided +if (!token) { + const tokenResponse = await fetch( + BASE_URL + "/get-new-token", { + method: 'post' + }); + if (tokenResponse.status === 200) { + jsonResponse = await tokenResponse.json(); + token = jsonResponse.token; + ea.setScriptSettings({token: {value: token, hidden: true}}); + } else { + notice(`Taskbone OCR Error: ${tokenResponse.status}\nPlease try again later.`); + return; + } +} + +//get image element +//if multiple image elements were selected prompt user to choose +const imageElements = ea.getViewSelectedElements().filter((el)=>el.type==="image"); + +//need to save the view to ensure recently pasted images are saved as files +await ea.targetView.save(); + +let selectedImageElement = null; +switch (imageElements.length) { + case 0: + return; + case 1: + selectedImageElement = imageElements[0]; + break; + default: + const files = imageElements.map((el)=>ea.getViewFileForImageElement(el)); + selectedImageElement = await utils.suggester(files.map((f)=>f.name),imageElements); + break; +} + +if(!selectedImageElement) { + notice("No image element was selected"); + return; +} +const imageFile = ea.getViewFileForImageElement(selectedImageElement); +if(!imageFile) { + notice("Can read image file"); + return; +} + +//Execute the OCR +let text = null; +const fileBuffer = await app.vault.readBinary(imageFile); +const formData = new FormData(); +formData.append("image", new Blob([fileBuffer])) +try { + const response = await fetch( + BASE_URL + "/get-text", { + headers: { + Authorization: "Bearer " + token + }, + method: "post", + body: formData + }); + if (response.status == 200) { + jsonResponse = await response.json(); + text = jsonResponse?.text; + } else { + notice(`Could not read Text from ${file.path}:\n Error: ${response.status}`); + return; + } +} catch (error) { + notice(`The OCR service seems unavailable right now. Please try again later.`); + return; +} + +if(!text) { + notice("No text found"); + return; +} +console.log({text}); + +//add text element to drawing +const id = ea.addText(selectedImageElement.x,selectedImageElement.y+selectedImageElement.height,text); +await ea.addElementsToView(); +ea.selectElementsInView([ea.getElement(id)]); +ea.getExcalidrawAPI().zoomToFit(ea.getViewSelectedElements(),1); + +//utility function +function notice(message) { + new Notice(message,10000); + console.log(message); +} diff --git a/ea-scripts/OCR - Optical Character Recognition.svg b/ea-scripts/Archive/OCR - Optical Character Recognition.svg similarity index 100% rename from ea-scripts/OCR - Optical Character Recognition.svg rename to ea-scripts/Archive/OCR - Optical Character Recognition.svg diff --git a/ea-scripts/TheBrain-navigation.md b/ea-scripts/Archive/TheBrain-navigation.md similarity index 97% rename from ea-scripts/TheBrain-navigation.md rename to ea-scripts/Archive/TheBrain-navigation.md index f52c1f1..5826b0e 100644 --- a/ea-scripts/TheBrain-navigation.md +++ b/ea-scripts/Archive/TheBrain-navigation.md @@ -1,1080 +1,1080 @@ -/* -An Excalidraw based graph user interface for your Vault. Requires the [Dataview plugin](https://github.com/blacksmithgu/obsidian-dataview). Generates a graph view similar to that of [TheBrain](https://TheBrain.com) plex. - -Watch introduction to this script on [YouTube](https://youtu.be/plYobK-VufM). - -![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/TheBrain.jpg) - -```javascript -*/ - -if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.6.25")) { - new Notice("This script requires a newer version of Excalidraw. Please install the latest version."); - return; -} - -if(!window.DataviewAPI) { - new Notice ("Some features will only work if you have the Dataview plugin installed and enabled", 4000); - return; -} - -const EVENT = "active-leaf-change"; - -const removeEventHandler = () => { - app.workspace.off(EVENT,window.brainGraphEventHandler); - if(isBoolean(window.excalidrawView?.linksAlwaysOpenInANewPane)) { - window.excalidrawView.linksAlwaysOpenInANewPane = false; - } - const ea = ExcalidrawAutomate; - ea.setView(window.excalidrawView); - if(ea.targetView?.excalidrawAPI) { - try { - ea.targetView.semaphores.saving = false; - ea.getExcalidrawAPI().updateScene({appState:{viewModeEnabled:false}}); - } catch {} - } - delete window.excalidrawView; - delete window.excalidrawFile; - delete window.lastfilePath; - new Notice("Brain Graph Off") - setTimeout(()=>delete window.brainGraphEventHandler); -} - -//Turn off event handler if it is already running -if(window.brainGraphEventHandler) { - removeEventHandler(); - return; -} - -//------------------------------------------------------- -// Load Settings -//------------------------------------------------------- -let saveSettings = false; -settings = ea.getScriptSettings(); -//set default values on first run -if(!settings["Max number of nodes/domain"]) { - settings = { - "Confirmation prompt at startup": { - value: true, - description: "Prompt me to confirm starting of the script because " + - "it will overwrite the current active drawing. " + - "You can disable this warning by turning off this switch" - }, - "Max number of nodes/domain": { - value: 30, - description: "Maximum number of items to show in each domain: parents, children, siblings, jumps." - }, - "Infer non-Breadcrumbs links": { - value: true, - description: "Links on the page are children, backlinks to the page are " + - "parents. Breadcrumbs take priority. Inferred nodes have a dashed border." - }, - "Hide attachments": { - value: true, - description: "Hide attachments. Will only have an effect if Infer non-Breadcrumbs links is turned on." - }, - "Font family": { - value: "Code", - valueset: ["Hand-drawn","Normal","Code","Fourth (custom) Font"] - }, - "Stroke roughness": { - value: "Architect", - valueset: ["Architect", "Artist", "Cartoonist"] - }, - "Rectangle stroke sharpness": { - value: "round", - valueset: ["sharp", "round"] - }, - "Central font size": { - value: 30, - description: "Font size of the central node" - }, - "Font size": { - value: 20, - description: "Font size of jumps, children and parents" - }, - "Siblings font size": { - value: 15, - description: "Font size of siblings" - }, - "Max label length": { - value: 30, - description: "Maximum number of characters to display from node title. Longer nodes will end with '...'" - }, - "Padding": { - value: 10, - description: "Padding of the node rectangle" - }, - "Gate offset": { - value: 15, - description: "The offset to the left and right of the parent and child gates." - }, - "Gate radius": { - value: 5, - description: "The radius of the 3 small circles (alias: gates) serving as connection points for nodes" - }, - "Canvas color": { - value: "hsl(208, 80%, 23%)", - description: "Any legal HTML color (#000000, rgb, color-name, etc.)." - }, - "Gate color": { - value: "white", - description: "Any legal HTML color (#000000, rgb, color-name, etc.)." - }, - "Link color": { - value: "hsl(0, 0%, 41%)", - description: "Any legal HTML color (#000000, rgb, color-name, etc.)." - }, - "Central-node background color": { - value: "#C49A13", - description: "Any legal HTML color (#000000, rgb, color-name, etc.)." - }, - "Central-node color": { - value: "black", - description: "Any legal HTML color (#000000, rgb, color-name, etc.)." - }, - "Breadcrumbs-node background color": { - value: "rgba(0,0,0,0.4)", - description: "Any legal HTML color (#000000, rgb, color-name, etc.)." - }, - "Breadcrumbs-node color": { - value: "white", - description: "Any legal HTML color (#000000, rgb, color-name, etc.)." - }, - "Non-breadcrumbs-node background color": { - value: "rgba(0,0,5,0.7)", - description: "Any legal HTML color (#000000, rgb, color-name, etc.)." - }, - "Non-breadcrumbs-node color": { - value: "hsl(208, 80%, 77%)", - description: "Any legal HTML color (#000000, rgb, color-name, etc.)." - }, - "Virtual-node background color": { - value: "rgba(255,0,0,0.4)", - description: "Any legal HTML color (#000000, rgb, color-name, etc.)." - }, - "Virtual-node color": { - value: "white", - description: "Any legal HTML color (#000000, rgb, color-name, etc.)." - } - }; - saveSettings = true; -} -if(!settings["Display alias if available"]) { - settings["Display alias if available"] = { - value: true, - description: "Displays the page alias instead of the " + - "filename if it is specified in the page's front matter. " - }; - saveSettings = true; -} -if(!settings["Graph settings JSON"]) { - settings["Graph settings JSON"] = { - height: "450px", - value: `{\n "breadcrumbs": {\n "down": ["children", "child"],\n "up": ["parents", "parent"],\n "jump": ["jump", "jumps"]\n },\n "tags": {\n "#excalidraw": {\n "nodeColor": "hsl(59, 80%, 77%)",\n "gateColor": "#fd7e14",\n "borderColor": "black",\n "backgroundColor": "rgba(50,50,50,0.5)",\n "prefix": "🎨 "\n },\n "#dnp": {\n "prefix": "🗓 "\n }\n }\n}`, - description: `This may contain two elements: -
    -
  1. A specification of your breadcrumbs hierarchy. Note, that if you have the Breadcrumbs plugin installed and enabled then TheBrain-navigation script will take your hierarchy settings from Breadcrumbs. If Breadcrumbs is disabled, this specification will be used.
  2. -
  3. Formatting of nodes based on page tags. You can specify special formatting rules for tags. If multiple tags are present on the page the first matching a specification will be used. You may provide partial specifications as well. e.g. if you only specify prefix, the other attributes will follow your default settings.
  4. -
-
{
-  "breadcrumbs": {
-    "down": ["children", "child"],
-    "up": ["parents", "parent"],
-    "jump": ["jump", "jumps"]
-  },
-  "tags": {
-    "#excalidraw": {
-      "nodeColor": "hsl(59, 80%, 77%)",
-      "gateColor": "#fd7e14",
-      "borderColor": "black",
-      "backgroundColor": "rgba(50,50,50,0.5)",
-      "prefix": "🎨 "
-    },
-    "#dnp": {
-      "prefix": "🗓 "
-    }
-  }
-}
-
` - }; - saveSettings = true; -} - -if(saveSettings) { - ea.setScriptSettings(settings); -} - -const SHOW_CONFIRMATION_PROMPT = settings["Confirmation prompt at startup"].value; -const MAX_ITEMS = Math.floor(settings["Max number of nodes/domain"].value)??40; -const INCLUDE_OBSIDIAN_LINKS = settings["Infer non-Breadcrumbs links"].value; -const HIDE_ATTACHMENTS = settings["Hide attachments"].value; -const FONT_FAMILY = settings["Font family"].value === "Hand-drawn" - ? 1 - : settings["Font family"].value === "Normal" - ? 2 - : settings["Font family"].value === "Code" - ? 3 - : 4; -const STROKE_ROUGHNESS = settings["Stroke roughness"].value === "Architect" - ? 0 - : settings["Stroke roughness"].value === "Artist" - ? 1 - : 2; -const STROKE_SHARPNESS = settings["Rectangle stroke sharpness"].value; -const CENTRAL_FONT_SIZE = Math.floor(settings["Central font size"].value)??30; -const FONT_SIZE = Math.floor(settings["Font size"].value)??20; -const DISTANT_FONT_SIZE = Math.floor(settings["Siblings font size"].value)??15; -const MAX_LABEL_LENGTH = Math.floor(settings["Max label length"].value)??30; -const PADDING = Math.floor(settings["Padding"].value)??10; -const GATE_OFFSET = Math.floor(settings["Gate offset"].value)??15; -const GATE_RADIUS = Math.floor(settings["Gate radius"].value)??5; -const BG_COLOR = settings["Canvas color"].value; -const GATE_COLOR = settings["Gate color"].value; -const LINK_COLOR = settings["Link color"].value; -const CENTRAL_NODE_BG_COLOR = settings["Central-node background color"].value; -const CENTRAL_NODE_COLOR = settings["Central-node color"].value; -const NODE_BG_COLOR = settings["Breadcrumbs-node background color"].value; -const NODE_COLOR = settings["Breadcrumbs-node color"].value; -const OBSIDIAN_NODE_BG_COLOR = settings["Non-breadcrumbs-node background color"].value; -const OBSIDIAN_NODE_COLOR = settings["Non-breadcrumbs-node color"].value; -const VIRTUAL_NODE_BG_COLOR = settings["Virtual-node background color"].value; -const VIRTUAL_NODE_COLOR = settings["Virtual-node color"].value; -const USE_ALIAS = settings["Display alias if available"].value; -let formattingJSON = {}; -try { - formattingJSON = JSON.parse(settings["Graph settings JSON"].value); -} catch (e) { - new Notice("Error reading graph settings JSON, see developer console for more information",4000); - console.log(e); -}; -const NODE_FORMATTING = formattingJSON?.tags??{}; -const FORMATTED_TAGS = Object.keys(NODE_FORMATTING); - -//------------------------------------------------------- -// Load breadcrumbs hierarchies -const HIERARCHIES = new Map(); -if(window.BCAPI) { //read breadcrumbs if available - const getHierarchyFields = (direction) => { - const values = new Set(direction); - direction.forEach( - d => BCAPI.plugin.settings.userHiers.forEach( - h => h[d].forEach( - x => values.add(x) - ) - ) - ); - return Array.from(values); - } - HIERARCHIES.set("down",getHierarchyFields(["down"])); - HIERARCHIES.set("up",getHierarchyFields(["up"])); - HIERARCHIES.set("jump",getHierarchyFields(["prev","next"])); -} else { - HIERARCHIES.set("down",formattingJSON?.breadcrumbs?.down??["down","child","children"]); - HIERARCHIES.set("up",formattingJSON?.breadcrumbs?.up??["up", "parent","parents"]); - HIERARCHIES.set("jump",formattingJSON?.breadcrumbs?.jump??["jump", "jumps", "next", "previous"]); -} - -//------------------------------------------------------- -// Initialization -//------------------------------------------------------- -if(SHOW_CONFIRMATION_PROMPT) { - const result = await utils.inputPrompt( - "This will overwrite the current active drawing", - "type: 'ok' to Continue" - ); - if(result !== "ok") return; -} - -const measureText = (text,fontSize) => { - ea.style.fontSize = fontSize; - return ea.measureText(text); -} - -ea.style.fontFamily = FONT_FAMILY; -const TEXT_SIZE = measureText("m".repeat(MAX_LABEL_LENGTH+3),FONT_SIZE); -const NODE_WIDTH = TEXT_SIZE.width + 3 * PADDING; -const NODE_HEIGHT = 2 * (TEXT_SIZE.height + 2 * PADDING); - -ea.getExcalidrawAPI().updateScene({ - appState: { - viewModeEnabled:true, - theme: "light", - viewBackgroundColor: BG_COLOR - }, - elements:[] -}); - -ea.style.strokeColor = NODE_COLOR; -ea.addText(0,0,"Open a document in another pane and click it to get started.\n\n" + - "For the best experience enable 'Open in adjacent pane'\nin Excalidraw settings " + - "under 'Links and Transclusion'.", {textAlign:"center"}); -await ea.addElementsToView(); -ea.getExcalidrawAPI().zoomToFit(); - -window.excalidrawView = ea.targetView; -window.excalidrawFile = ea.targetView.file; - -ea.targetView.linksAlwaysOpenInANewPane = true; - -new Notice("Brain Graph On"); - -//------------------------------------------------------- -// Supporting functions and classes -//------------------------------------------------------- -const getFilenameFromPath = (path) => { - const mdFile = path.endsWith(".md"); - const filename = path.substring(path.lastIndexOf("/")+1); - return mdFile ? filename.slice(0,-3) : filename; -} - -const getExtension = (path) => { - const lastDot = path.lastIndexOf("."); - if(lastDot === -1) return "md"; - return path.slice(lastDot+1); -} - -const distinct = (data) => Array.from(new Set(data)); - -class Layout { - constructor(spec) { - this.spec = spec; - this.nodes = []; - this.renderedNodes = []; - } - - layout(columns = this.spec.columns) { - const generateLayoutVector = (pattern) => { - const res = []; - let cur = 1; - let state = true; - pattern - .map(p => Math.floor(p)) - .forEach(cnt => { - for(let i=0;i items%2 - ? generateLayoutVector([(columns-items)/2,items,(columns-items)/2]) - : generateLayoutVector([(columns-items)/2,items/2,1,items/2,(columns-items)/2]); - - const sortedNodes = this.nodes.sort((a,b) => a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1) - const itemCount = sortedNodes.length; - if(itemCount === 0) return; - const rowCount = Math.ceil(itemCount / columns); - this.renderedNodes = Array(rowCount).fill().map((_,i) => - (i+1 < rowCount) || (itemCount % columns === 0) - ? Array(columns).fill().map((_,j) => sortedNodes[i*columns+j]) //full row - : getRowLayout(itemCount % columns).map(idx => idx ? sortedNodes[i*columns+idx-1]:null)); - } - - render() { - this.layout(); - const rows = this.renderedNodes.length; - const height = rows * this.spec.rowHeight; - const top = (this.spec.top === null && this.spec.bottom === null) //unconstrained - ? this.spec.origoY - height/2 - : this.spec.top !== null - ? (this.spec.origoY - height/2) < this.spec.top //top constrained - ? this.spec.top - : this.spec.origoY - height/2 - : (this.spec.origoY + height/2) > this.spec.bottom //bottom constrained - ? this.spec.bottom - height - : this.spec.origoY - height/2; - const center00 = { - x: this.spec.origoX - (this.spec.columns === 1 ? 0 : (this.spec.columns-1)/2*this.spec.columnWidth), - y: top - }; - this.renderedNodes.forEach((nodes,row) => - nodes.forEach((node,idx) => { - if(!node) return; - node.setCenter({ - x: center00.x + idx*this.spec.columnWidth, - y: center00.y + row*this.spec.rowHeight - }); - node.render(); - }) - ); - } -} - -class Node { - constructor(spec) { - const dvPage = spec.file ? DataviewAPI?.page(spec.file.path) : null; - const tag = (dvPage?.file?.tags?.values??[]).filter(t=>FORMATTED_TAGS.some(x=>t.startsWith(x)))[0]; - if(tag) { - const format = NODE_FORMATTING[FORMATTED_TAGS.filter(x=>tag.startsWith(x))[0]]; - spec.gateColor = format.gateColor ?? spec.gateColor; - spec.backgroundColor = format.backgroundColor ?? spec.backgroundColor; - spec.nodeColor = format.nodeColor ?? spec.nodeColor; - spec.borderColor = format.borderColor ?? spec.borderColor; - spec.prefix = format.prefix; - } - this.spec = spec; - - const aliases = (spec.file && USE_ALIAS) - ? (dvPage?.file?.aliases?.values??[]) - : []; - const label = (spec.prefix??"") + (aliases.length > 0 - ? aliases[0] - : (spec.file - ? (spec.file.extension === "md" ? spec.file.basename : spec.file.name) - : spec.nodeTitle)); - this.label = label.length > spec.maxLabelLength - ? label.substring(0,spec.maxLabelLength-1) + "..." - : label; - this.labelSize = measureText(this.label, spec.fontSize); - } - - setCenter(center) { - this.center = center; - } - - render() { - ea.style.fontSize = this.spec.fontSize; - ea.style.strokeColor = this.spec.file - ? this.spec.nodeColor - : this.spec.virtualNodeColor; - ea.style.backgroundColor = "transparent"; - this.id = ea.addText( - this.center.x - this.labelSize.width / 2, - this.center.y - this.labelSize.height / 2, - this.label, - { - wrapAt: this.spec.maxLabelLength+5, - textAlign: "center", - box: true, - boxPadding: this.spec.padding - } - ); - const box = ea.getElement(this.id); - box.link = `[[${this.spec.file?.path??this.spec.nodeTitle}]]`; - box.backgroundColor = this.spec.file - ? this.spec.backgroundColor - : this.spec.virtualNodeBGColor; - box.strokeColor = this.spec.borderColor; - box.strokeStyle = this.spec.strokeStyle??"solid"; - - ea.style.strokeColor = this.spec.gateColor; - ea.style.backgroundColor = this.spec.hasJumps ? this.spec.gateColor : "transparent"; - this.jumpGateId = ea.addEllipse( - this.spec.jumpOnLeft - ? this.center.x - this.spec.gateRadius * 2 - this.spec.padding - this.labelSize.width / 2 - : this.center.x + this.spec.padding + this.labelSize.width / 2, - this.center.y - this.spec.gateRadius, - this.spec.gateRadius * 2, - this.spec.gateRadius * 2 - ); - ea.style.backgroundColor = this.spec.hasParents ? this.spec.gateColor : "transparent"; - this.parentGateId = ea.addEllipse( - this.center.x - this.spec.gateRadius - this.spec.gateOffset, - this.center.y - 2 * this.spec.gateRadius - this.spec.padding - this.labelSize.height / 2, - this.spec.gateRadius * 2, - this.spec.gateRadius * 2 - ); - ea.style.backgroundColor = this.spec.hasChildren ? this.spec.gateColor : "transparent"; - this.childGateId = ea.addEllipse( - this.center.x - this.spec.gateRadius + this.spec.gateOffset, - this.center.y + this.spec.padding + this.labelSize.height / 2, - this.spec.gateRadius * 2, - this.spec.gateRadius * 2 - ); - - ea.addToGroup([this.jumpGateId,this.parentGateId,this.childGateId,this.id, box.boundElements[0].id]); - } -} - -const addNodes = (nodesMap, root, nodes, layout, options) => { - nodes.forEach(filePath => { - const node = new Node({ - nodeTitle: getFilenameFromPath(filePath), - file: app.vault.getAbstractFileByPath(filePath), - hasChildren: false, - hasParents: false, - hasJumps: false, - ...options - }); - nodesMap.set(filePath,node); - layout.nodes.push(node); - }); -} - -//------------------------------------------------------- -// Breadcrumbs workaround to handle full filepath -//------------------------------------------------------- -const getPathOrSelf = (link,host) => { - return (app.metadataCache.getFirstLinkpathDest(link,host)?.path)??link; -} - -const readDVField = (field,file) => { - const res = new Set(); - if(field.values) { - field.values.forEach(l=>{ - if(l.type === "file") res.add(getPathOrSelf(l.path,file.path)); - }); - return Array.from(res); - } - if(field.path) { - return [getPathOrSelf(field.path,file.path)]; - } - const m = field.matchAll(/[^[]*\[\[([^#\]\|]*)[^\]]*]]/g); - while(!(r=m.next()).done) { - if(r.value[1]) { - res.add(getPathOrSelf(r.value[1],file.path)); - } - } - return Array.from(res); -} - -const getDVFieldLinks = (page,dir,retSet=false) => { - const fields = HIERARCHIES.get(dir); - const links = new Set(); - const processed = new Set(); - fields.forEach(f => { - if(page[f] && !processed.has(f)) { - processed.add(f); - readDVField(page[f],page.file).forEach(l=>links.add(l)) - }; - }); - return retSet ? links : Array.from(links); -} - -//------------------------------------------------------- -// Event handler -//------------------------------------------------------- -window.brainGraphEventHandler = async (leaf) => { - //sleep for 100ms to give time for leaf.view.file to be set - await new Promise((resolve) => setTimeout(resolve, 100)); - - //------------------------------------------------------- - //terminate event handler if view no longer exists or file has changed - if(!window.excalidrawView?.file || window.excalidrawView.file.path !== window.excalidrawFile?.path) { - removeEventHandler(); - return; - } - - if(!leaf?.view?.file) return; - const rootFile = leaf.view.file; - - if (rootFile.path === window.excalidrawFile.path) return; //brainview drawing is active - - if(window.lastfilePath && window.lastfilePath === rootFile.path) return; //don't reload the file if it has not changed - window.lastfilePath = rootFile.path; - - //------------------------------------------------------- - //I must reinitialize ea because in the event handler the script engine ea will no longer be available - ea = ExcalidrawAutomate; - ea.reset(); - ea.setView(window.excalidrawView); - ea.style.fontFamily = FONT_FAMILY; - ea.style.roughness = STROKE_ROUGHNESS; - ea.style.strokeSharpness = STROKE_SHARPNESS; - ea.getExcalidrawAPI().updateScene({elements:[]}); - ea.style.verticalAlign = "middle"; - ea.style.strokeSharpness = "round"; - ea.style.fillStyle = "solid"; - - //------------------------------------------------------- - //build list of nodes - const dvPage = DataviewAPI.page(rootFile.path); //workaround because - const parentsSet = dvPage - ? getDVFieldLinks(dvPage,"up",true) - : new Set(); - parentsSet.delete(rootFile.path); - - const childrenSet = dvPage - ? getDVFieldLinks(dvPage,"down",true) - : new Set(); - childrenSet.delete(rootFile.path); - - const jumpsSet = dvPage - ? getDVFieldLinks(dvPage,"jump",true) - : new Set(); - jumpsSet.delete(rootFile.path); - - let backlinksSet = new Set( - Object.keys((app.metadataCache.getBacklinksForFile(rootFile)?.data)??{}) - .map(l=>app.metadataCache.getFirstLinkpathDest(l,rootFile.path)?.path??l) - ); - - backlinksSet.forEach(l=>{ - const page = DataviewAPI.page(l); - if(page) { - if(getDVFieldLinks(page,"down",true).has(rootFile.path)) { - parentsSet.add(l); - backlinksSet.delete(l); - return; - } - if(getDVFieldLinks(page,"up",true).has(rootFile.path)) { - childrenSet.add(l); - backlinksSet.delete(l); - return; - } - if(getDVFieldLinks(page,"jump", true).has(rootFile.path)) { - jumpsSet.add(l); - backlinksSet.delete(l); - return; - } - } - }) - - const parents = Array.from(parentsSet).slice(0,MAX_ITEMS); - const children = Array.from(childrenSet).slice(0,MAX_ITEMS); - const jumps = Array.from(jumpsSet).slice(0,MAX_ITEMS); - - //adding links from the document, not explicitly declared as a breadcrumb - const forwardLinks = INCLUDE_OBSIDIAN_LINKS - ? distinct(app.metadataCache - .getLinks()[rootFile.path]?.map( - l=>app.metadataCache.getFirstLinkpathDest(l.link.match(/[^#]*/)[0],rootFile.path)??l.link.match(/[^#]*/)[0] - ).filter(f=>f && (!HIDE_ATTACHMENTS || (f.extension??getExtension(f)) === "md")) - .map(f=>f.path??f) - .filter(l=>!parentsSet.has(l) && !childrenSet.has(l) && !jumpsSet.has(l) && l!==rootFile.path && !backlinksSet.has(l)) - .slice(0,Math.max(MAX_ITEMS-children.length)) - ) - : []; - const forwardlinksSet = new Set(forwardLinks); - - const backLinks = INCLUDE_OBSIDIAN_LINKS - ? Array.from(backlinksSet) - .filter(l=>!parentsSet.has(l) && !childrenSet.has(l) && - !jumpsSet.has(l) && l!==rootFile.path && !forwardlinksSet.has(l)) - .slice(0,Math.max(MAX_ITEMS-parents.length)) - : []; - backlinksSet = new Set(backLinks); - - const sharedParents = INCLUDE_OBSIDIAN_LINKS - ? backLinks.concat(parents) - : parents; - - const siblings = distinct( - sharedParents.map(p=>{ - const page = DataviewAPI.page(p); - return page - ? getDVFieldLinks(page,"down") - .filter(l=>!parentsSet.has(l) && !childrenSet.has(l) && - !jumpsSet.has(l) && l!==rootFile.path && - !forwardlinksSet.has(l) && !backlinksSet.has(l)) - : []; - }).flat() - ).slice(0,MAX_ITEMS); - const siblingsSet = new Set(siblings); - - const inverseInferredSiblingsMap = new Map(); - const inferredSiblings = INCLUDE_OBSIDIAN_LINKS - ? distinct( - sharedParents.map(p=>{ - f = app.vault.getAbstractFileByPath(p); //app.metadataCache.getFirstLinkpathDest(p,rootFile.path); - if(!f) { - return []; - } - const res = Object.keys((app.metadataCache.getBacklinksForFile(f)?.data)??{}) - .map(l=>app.metadataCache.getFirstLinkpathDest(l,rootFile.path)?.path??l) - .filter(l=>!parentsSet.has(l) && !childrenSet.has(l) && !jumpsSet.has(l) && - l!==rootFile.path && !forwardlinksSet.has(l) && !backlinksSet.has(l) && !siblingsSet.has(l)); - res.forEach(r=>inverseInferredSiblingsMap.set(r,p)); - return res; - }).flat() - ).slice(0,Math.max(MAX_ITEMS-siblings.length)) - : []; - const inferredSiblingsSet = new Set(inferredSiblings); - - //------------------------------------------------------- - // Generate layout and nodes - const nodesMap = new Map(); - const linksMap = new Map(); - - const lCenter = new Layout({ - origoX: 0, - origoY: 0, - top: null, - bottom: null, - columns: 1, - columnWidth: NODE_WIDTH, - rowHeight: NODE_HEIGHT - }); - - const manyChildren = (children.length + forwardLinks.length) >10; - const manySiblings = (siblings.length + inferredSiblings.length) > 10; - const singleParent = (parents.length + backLinks.length) <= 1 - - const lChildren = new Layout({ - origoX: 0, - origoY: 2.5 * NODE_HEIGHT, - top: 2.5 * NODE_HEIGHT - NODE_HEIGHT/2, - bottom: null, - columns: manyChildren ? 5 : 3, - columnWidth: NODE_WIDTH, - rowHeight: NODE_HEIGHT - }); - - const lJumps = new Layout({ - origoX: (manyChildren ? -3 : -2) * NODE_WIDTH, - origoY: 0, - top: null, - bottom: null, - columns: 1, - columnWidth: NODE_WIDTH, - rowHeight: NODE_HEIGHT - }); - - const lParents = new Layout({ - origoX: 0, - origoY: -2.5 * NODE_HEIGHT, - top: null, - bottom: -2.5 * NODE_HEIGHT + NODE_HEIGHT/2, - columns: 3, - columnWidth: NODE_WIDTH, - rowHeight: NODE_HEIGHT - }); - - const lSiblings = new Layout({ - origoX: NODE_WIDTH * ((singleParent ? 0 : 1) + (manySiblings ? 2 : 1)), - origoY: -2.5 * NODE_HEIGHT, - top: null, - bottom: NODE_HEIGHT, - columns: (manySiblings ? 3 : 1), - columnWidth: NODE_WIDTH, - rowHeight: NODE_HEIGHT - }) - - const rootNode = new Node({ - file: rootFile, - hasChildren: false, - hasParents: false, - hasJumps: false, - fontSize: CENTRAL_FONT_SIZE, - jumpOnLeft: true, - maxLabelLength: 2*MAX_LABEL_LENGTH, - gateRadius: GATE_RADIUS, - gateOffset: GATE_OFFSET, - padding: PADDING, - nodeColor: CENTRAL_NODE_COLOR, - gateColor: GATE_COLOR, - borderColor: CENTRAL_NODE_COLOR, - backgroundColor: CENTRAL_NODE_BG_COLOR, - virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR - }); - nodesMap.set(rootFile.path,rootNode); - lCenter.nodes.push(rootNode); - - addNodes( - nodesMap, - rootFile, - parents, - lParents, - { - fontSize: FONT_SIZE, - jumpOnLeft: true, - maxLabelLength: MAX_LABEL_LENGTH, - gateRadius: GATE_RADIUS, - gateOffset: GATE_OFFSET, - padding: PADDING, - nodeColor: NODE_COLOR, - gateColor: GATE_COLOR, - borderColor: "transparent", - backgroundColor: NODE_BG_COLOR, - virtualNodeColor: VIRTUAL_NODE_COLOR, - virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR - } - ); - - addNodes( - nodesMap, - rootFile, - backLinks, - lParents, - { - fontSize: FONT_SIZE, - jumpOnLeft: true, - maxLabelLength: MAX_LABEL_LENGTH, - gateRadius: GATE_RADIUS, - gateOffset: GATE_OFFSET, - padding: PADDING, - nodeColor: OBSIDIAN_NODE_COLOR, - gateColor: GATE_COLOR, - borderColor: OBSIDIAN_NODE_BG_COLOR, - strokeStyle: "dotted", - strokeWidth: 3, - backgroundColor: OBSIDIAN_NODE_BG_COLOR, - virtualNodeColor: VIRTUAL_NODE_COLOR, - virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR - } - ); - - addNodes( - nodesMap, - rootFile, - children, - lChildren, - { - fontSize: FONT_SIZE, - jumpOnLeft: true, - maxLabelLength: MAX_LABEL_LENGTH, - gateRadius: GATE_RADIUS, - gateOffset: GATE_OFFSET, - padding: PADDING, - nodeColor: NODE_COLOR, - gateColor: GATE_COLOR, - borderColor: "transparent", - backgroundColor: NODE_BG_COLOR, - virtualNodeColor: VIRTUAL_NODE_COLOR, - virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR - } - ); - - addNodes( - nodesMap, - rootFile, - forwardLinks, - lChildren, - { - fontSize: FONT_SIZE, - jumpOnLeft: true, - maxLabelLength: MAX_LABEL_LENGTH, - gateRadius: GATE_RADIUS, - gateOffset: GATE_OFFSET, - padding: PADDING, - nodeColor: OBSIDIAN_NODE_COLOR, - gateColor: GATE_COLOR, - borderColor: OBSIDIAN_NODE_BG_COLOR, - strokeStyle: "dotted", - strokeWidth: 3, - backgroundColor: OBSIDIAN_NODE_BG_COLOR, - virtualNodeColor: VIRTUAL_NODE_COLOR, - virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR - } - ); - - addNodes( - nodesMap, - rootFile, - jumps, - lJumps, - { - fontSize: FONT_SIZE, - jumpOnLeft: false, - maxLabelLength: MAX_LABEL_LENGTH, - gateRadius: GATE_RADIUS, - gateOffset: GATE_OFFSET, - padding: PADDING, - nodeColor: NODE_COLOR, - gateColor: GATE_COLOR, - borderColor: "transparent", - backgroundColor: NODE_BG_COLOR, - virtualNodeColor: VIRTUAL_NODE_COLOR, - virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR - } - ); - - addNodes( - nodesMap, - rootFile, - siblings, - lSiblings, - { - fontSize: DISTANT_FONT_SIZE, - jumpOnLeft: true, - maxLabelLength: MAX_LABEL_LENGTH, - gateRadius: GATE_RADIUS, - gateOffset: GATE_OFFSET, - padding: PADDING, - nodeColor: NODE_COLOR, - gateColor: GATE_COLOR, - borderColor: "transparent", - backgroundColor: NODE_BG_COLOR, - virtualNodeColor: VIRTUAL_NODE_COLOR, - virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR - } - ); - - addNodes( - nodesMap, - rootFile, - inferredSiblings, - lSiblings, - { - fontSize: DISTANT_FONT_SIZE, - jumpOnLeft: true, - maxLabelLength: MAX_LABEL_LENGTH, - gateRadius: GATE_RADIUS, - gateOffset: GATE_OFFSET, - padding: PADDING, - nodeColor: OBSIDIAN_NODE_COLOR, - gateColor: GATE_COLOR, - borderColor: OBSIDIAN_NODE_BG_COLOR, - strokeStyle: "dotted", - strokeWidth: 3, - backgroundColor: OBSIDIAN_NODE_BG_COLOR, - virtualNodeColor: VIRTUAL_NODE_COLOR, - virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR - } - ); - - //------------------------------------------------------- - // Generate links for all displayed nodes - const separator = "?"; //any character disallowed in filenames - Array.from(nodesMap.keys()).forEach(filePath => { - const node = nodesMap.get(filePath); - const dvPage = DataviewAPI.page(filePath); - - //------------------------------------------------------- - // Add links originating from node - const parent = forwardlinksSet.has(filePath) - ? rootFile.path - : inverseInferredSiblingsMap.get(filePath); - const parents = dvPage - ? (parent ? getDVFieldLinks(dvPage,"up",true).add(parent) : getDVFieldLinks(dvPage,"up",true)) - : (parent ? new Set([parent]) : new Set()); - - const child = backlinksSet.has(filePath) ? rootFile.path : null - const children = dvPage - ? (child ? getDVFieldLinks(dvPage,"down", true).add(child) : getDVFieldLinks(dvPage,"down", true)) - : (child ? new Set([child]) : new Set()); - - const jumps = dvPage - ? getDVFieldLinks(dvPage,"jump", true) - : new Set(); - - //siblings are left out in this case on purpose - - const addLinks = (nodes,relation) => nodes.forEach(n => { - if(!nodesMap.has(n)) return; - if(linksMap.has(filePath + separator + n)) return; - linksMap.set(filePath + separator + n,relation); - linksMap.set(n + separator + filePath,null); - }); - - addLinks(parents,["parent","child"]); - addLinks(children, ["child","parent"]); - addLinks(jumps, ["jump","jump"]); - - //------------------------------------------------------- - //Set gate fill - //- if node has outgoing links - node.spec.hasChildren = node.spec.hasChildren || children.size>0; - node.spec.hasParents = node.spec.hasParents || parents.size>0; - node.spec.hasJumps = node.spec.hasJumps || jumps.size>0; - - //- of the nodes on the other end of outgoing links - // this is required on top of backlinks, to handle - // nodes for referenced but not yet created pages - parents.forEach(p=>{ - const n = nodesMap.get(p); - if(n) n.spec.hasChildren = true; - }); - - children.forEach(p=>{ - const n = nodesMap.get(p); - if(n) n.spec.hasParents = true; - }); - - jumps.forEach(p=>{ - const n = nodesMap.get(p); - if(n) n.spec.hasJumps = true; - }); - - //- if node has an incoming link from a node outside the visible graph - if(node.spec.file && !(node.spec.hasParents && node.spec.hasChildren && node.hasJumps)) { - const nodeBacklinks = new Set( - Object.keys((app.metadataCache.getBacklinksForFile(node.spec.file)?.data)??{}) - .map(l=>app.metadataCache.getFirstLinkpathDest(l,filePath)?.path??l) - ); - - nodeBacklinks.forEach(l=>{ - if(node.spec.hasParents && node.spec.hasChildren && node.hasJumps) return; - const page = DataviewAPI.page(l); - if(page) { - if(getDVFieldLinks(page,"down",true).has(filePath)) { - node.spec.hasParents = true; - return; - } - if(getDVFieldLinks(page,"up",true).has(filePath)) { - node.spec.hasChildren = true; - return; - } - if(getDVFieldLinks(page,"jump",true).has(filePath)) { - node.spec.hasJumps = true; - return; - } - if(INCLUDE_OBSIDIAN_LINKS) { - node.spec.hasParents = true; - return; - } - } - }); - } - - if(!node.spec.hasChildren && INCLUDE_OBSIDIAN_LINKS && node.spec.file) { - const forwardLinks = app.metadataCache - .getLinks()[filePath]?.map( - l=>app.metadataCache.getFirstLinkpathDest(l.link.match(/[^#]*/)[0],rootFile.path)??l.link.match(/[^#]*/)[0] - ).filter(f=>f && (!HIDE_ATTACHMENTS || (f.extension??getExtension(f)) === "md")) - .map(f=>f.path??f) - .filter(l=>!parents.has(l) && !children.has(l) && !jumps.has(l) && l!==rootFile.path); - if(forwardLinks.length > 0) { - node.spec.hasChildren = true; - } - } - }); - - //------------------------------------------------------- - // Render - lCenter.render(); - lParents.render(); - lChildren.render(); - lJumps.render(); - lSiblings.render(); - - const getGate = (key,value) => (value === "parent") - ? nodesMap.get(key).parentGateId - : (value === "child") - ? nodesMap.get(key).childGateId - : nodesMap.get(key).jumpGateId; - - ea.style.strokeColor = LINK_COLOR; - for([key,value] of linksMap) { - if(value) { - const k=key.split(separator); - const gate1 = getGate(k[0],value[0]); - const gate2 = getGate(k[1],value[1]); - ea.connectObjects(gate1, null, gate2, null, { startArrowHead: null, endArrowHead: null }); - } - } - - elements = ea.getElements(); - ea.getExcalidrawAPI().updateScene({ - elements: elements.filter( - el=>el.type==="arrow" - ).concat(elements.filter(el=>el.type!=="arrow")) - }) - ea.getExcalidrawAPI().zoomToFit(); -} - -//------------------------------------------------------- -// Initiate event listner and trigger plex if file is open -//------------------------------------------------------- -app.workspace.on(EVENT, window.brainGraphEventHandler); - -ea.targetView.semaphores.saving = true; //disable saving by setting this Excalidraw flag (not published API) - -const mdLeaf = app.workspace.getLeavesOfType("markdown"); -if(mdLeaf.length>0) { - window.brainGraphEventHandler(mdLeaf[0]); - return; -} - -const mdExcalidrawLeaf = app.workspace.getLeavesOfType("excalidraw").filter(l=>l!==ea.targetView.leaf); -if(mdExcalidrawLeaf.length>0) { - window.brainGraphEventHandler(mdExcalidrawLeaf[0]); - return; -} - -const mdImageLeaf = app.workspace.getLeavesOfType("image"); -if(mdImageLeaf.length>0) { - window.brainGraphEventHandler(mdImageLeaf[0]); - return; +/* +An Excalidraw based graph user interface for your Vault. Requires the [Dataview plugin](https://github.com/blacksmithgu/obsidian-dataview). Generates a graph view similar to that of [TheBrain](https://TheBrain.com) plex. + +Watch introduction to this script on [YouTube](https://youtu.be/plYobK-VufM). + +![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/TheBrain.jpg) + +```javascript +*/ + +if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.6.25")) { + new Notice("This script requires a newer version of Excalidraw. Please install the latest version."); + return; +} + +if(!window.DataviewAPI) { + new Notice ("Some features will only work if you have the Dataview plugin installed and enabled", 4000); + return; +} + +const EVENT = "active-leaf-change"; + +const removeEventHandler = () => { + app.workspace.off(EVENT,window.brainGraphEventHandler); + if(isBoolean(window.excalidrawView?.linksAlwaysOpenInANewPane)) { + window.excalidrawView.linksAlwaysOpenInANewPane = false; + } + const ea = ExcalidrawAutomate; + ea.setView(window.excalidrawView); + if(ea.targetView?.excalidrawAPI) { + try { + ea.targetView.semaphores.saving = false; + ea.getExcalidrawAPI().updateScene({appState:{viewModeEnabled:false}}); + } catch {} + } + delete window.excalidrawView; + delete window.excalidrawFile; + delete window.lastfilePath; + new Notice("Brain Graph Off") + setTimeout(()=>delete window.brainGraphEventHandler); +} + +//Turn off event handler if it is already running +if(window.brainGraphEventHandler) { + removeEventHandler(); + return; +} + +//------------------------------------------------------- +// Load Settings +//------------------------------------------------------- +let saveSettings = false; +settings = ea.getScriptSettings(); +//set default values on first run +if(!settings["Max number of nodes/domain"]) { + settings = { + "Confirmation prompt at startup": { + value: true, + description: "Prompt me to confirm starting of the script because " + + "it will overwrite the current active drawing. " + + "You can disable this warning by turning off this switch" + }, + "Max number of nodes/domain": { + value: 30, + description: "Maximum number of items to show in each domain: parents, children, siblings, jumps." + }, + "Infer non-Breadcrumbs links": { + value: true, + description: "Links on the page are children, backlinks to the page are " + + "parents. Breadcrumbs take priority. Inferred nodes have a dashed border." + }, + "Hide attachments": { + value: true, + description: "Hide attachments. Will only have an effect if Infer non-Breadcrumbs links is turned on." + }, + "Font family": { + value: "Code", + valueset: ["Hand-drawn","Normal","Code","Fourth (custom) Font"] + }, + "Stroke roughness": { + value: "Architect", + valueset: ["Architect", "Artist", "Cartoonist"] + }, + "Rectangle stroke sharpness": { + value: "round", + valueset: ["sharp", "round"] + }, + "Central font size": { + value: 30, + description: "Font size of the central node" + }, + "Font size": { + value: 20, + description: "Font size of jumps, children and parents" + }, + "Siblings font size": { + value: 15, + description: "Font size of siblings" + }, + "Max label length": { + value: 30, + description: "Maximum number of characters to display from node title. Longer nodes will end with '...'" + }, + "Padding": { + value: 10, + description: "Padding of the node rectangle" + }, + "Gate offset": { + value: 15, + description: "The offset to the left and right of the parent and child gates." + }, + "Gate radius": { + value: 5, + description: "The radius of the 3 small circles (alias: gates) serving as connection points for nodes" + }, + "Canvas color": { + value: "hsl(208, 80%, 23%)", + description: "Any legal HTML color (#000000, rgb, color-name, etc.)." + }, + "Gate color": { + value: "white", + description: "Any legal HTML color (#000000, rgb, color-name, etc.)." + }, + "Link color": { + value: "hsl(0, 0%, 41%)", + description: "Any legal HTML color (#000000, rgb, color-name, etc.)." + }, + "Central-node background color": { + value: "#C49A13", + description: "Any legal HTML color (#000000, rgb, color-name, etc.)." + }, + "Central-node color": { + value: "black", + description: "Any legal HTML color (#000000, rgb, color-name, etc.)." + }, + "Breadcrumbs-node background color": { + value: "rgba(0,0,0,0.4)", + description: "Any legal HTML color (#000000, rgb, color-name, etc.)." + }, + "Breadcrumbs-node color": { + value: "white", + description: "Any legal HTML color (#000000, rgb, color-name, etc.)." + }, + "Non-breadcrumbs-node background color": { + value: "rgba(0,0,5,0.7)", + description: "Any legal HTML color (#000000, rgb, color-name, etc.)." + }, + "Non-breadcrumbs-node color": { + value: "hsl(208, 80%, 77%)", + description: "Any legal HTML color (#000000, rgb, color-name, etc.)." + }, + "Virtual-node background color": { + value: "rgba(255,0,0,0.4)", + description: "Any legal HTML color (#000000, rgb, color-name, etc.)." + }, + "Virtual-node color": { + value: "white", + description: "Any legal HTML color (#000000, rgb, color-name, etc.)." + } + }; + saveSettings = true; +} +if(!settings["Display alias if available"]) { + settings["Display alias if available"] = { + value: true, + description: "Displays the page alias instead of the " + + "filename if it is specified in the page's front matter. " + }; + saveSettings = true; +} +if(!settings["Graph settings JSON"]) { + settings["Graph settings JSON"] = { + height: "450px", + value: `{\n "breadcrumbs": {\n "down": ["children", "child"],\n "up": ["parents", "parent"],\n "jump": ["jump", "jumps"]\n },\n "tags": {\n "#excalidraw": {\n "nodeColor": "hsl(59, 80%, 77%)",\n "gateColor": "#fd7e14",\n "borderColor": "black",\n "backgroundColor": "rgba(50,50,50,0.5)",\n "prefix": "🎨 "\n },\n "#dnp": {\n "prefix": "🗓 "\n }\n }\n}`, + description: `This may contain two elements: +
    +
  1. A specification of your breadcrumbs hierarchy. Note, that if you have the Breadcrumbs plugin installed and enabled then TheBrain-navigation script will take your hierarchy settings from Breadcrumbs. If Breadcrumbs is disabled, this specification will be used.
  2. +
  3. Formatting of nodes based on page tags. You can specify special formatting rules for tags. If multiple tags are present on the page the first matching a specification will be used. You may provide partial specifications as well. e.g. if you only specify prefix, the other attributes will follow your default settings.
  4. +
+
{
+  "breadcrumbs": {
+    "down": ["children", "child"],
+    "up": ["parents", "parent"],
+    "jump": ["jump", "jumps"]
+  },
+  "tags": {
+    "#excalidraw": {
+      "nodeColor": "hsl(59, 80%, 77%)",
+      "gateColor": "#fd7e14",
+      "borderColor": "black",
+      "backgroundColor": "rgba(50,50,50,0.5)",
+      "prefix": "🎨 "
+    },
+    "#dnp": {
+      "prefix": "🗓 "
+    }
+  }
+}
+
` + }; + saveSettings = true; +} + +if(saveSettings) { + ea.setScriptSettings(settings); +} + +const SHOW_CONFIRMATION_PROMPT = settings["Confirmation prompt at startup"].value; +const MAX_ITEMS = Math.floor(settings["Max number of nodes/domain"].value)??40; +const INCLUDE_OBSIDIAN_LINKS = settings["Infer non-Breadcrumbs links"].value; +const HIDE_ATTACHMENTS = settings["Hide attachments"].value; +const FONT_FAMILY = settings["Font family"].value === "Hand-drawn" + ? 1 + : settings["Font family"].value === "Normal" + ? 2 + : settings["Font family"].value === "Code" + ? 3 + : 4; +const STROKE_ROUGHNESS = settings["Stroke roughness"].value === "Architect" + ? 0 + : settings["Stroke roughness"].value === "Artist" + ? 1 + : 2; +const STROKE_SHARPNESS = settings["Rectangle stroke sharpness"].value; +const CENTRAL_FONT_SIZE = Math.floor(settings["Central font size"].value)??30; +const FONT_SIZE = Math.floor(settings["Font size"].value)??20; +const DISTANT_FONT_SIZE = Math.floor(settings["Siblings font size"].value)??15; +const MAX_LABEL_LENGTH = Math.floor(settings["Max label length"].value)??30; +const PADDING = Math.floor(settings["Padding"].value)??10; +const GATE_OFFSET = Math.floor(settings["Gate offset"].value)??15; +const GATE_RADIUS = Math.floor(settings["Gate radius"].value)??5; +const BG_COLOR = settings["Canvas color"].value; +const GATE_COLOR = settings["Gate color"].value; +const LINK_COLOR = settings["Link color"].value; +const CENTRAL_NODE_BG_COLOR = settings["Central-node background color"].value; +const CENTRAL_NODE_COLOR = settings["Central-node color"].value; +const NODE_BG_COLOR = settings["Breadcrumbs-node background color"].value; +const NODE_COLOR = settings["Breadcrumbs-node color"].value; +const OBSIDIAN_NODE_BG_COLOR = settings["Non-breadcrumbs-node background color"].value; +const OBSIDIAN_NODE_COLOR = settings["Non-breadcrumbs-node color"].value; +const VIRTUAL_NODE_BG_COLOR = settings["Virtual-node background color"].value; +const VIRTUAL_NODE_COLOR = settings["Virtual-node color"].value; +const USE_ALIAS = settings["Display alias if available"].value; +let formattingJSON = {}; +try { + formattingJSON = JSON.parse(settings["Graph settings JSON"].value); +} catch (e) { + new Notice("Error reading graph settings JSON, see developer console for more information",4000); + console.log(e); +}; +const NODE_FORMATTING = formattingJSON?.tags??{}; +const FORMATTED_TAGS = Object.keys(NODE_FORMATTING); + +//------------------------------------------------------- +// Load breadcrumbs hierarchies +const HIERARCHIES = new Map(); +if(window.BCAPI) { //read breadcrumbs if available + const getHierarchyFields = (direction) => { + const values = new Set(direction); + direction.forEach( + d => BCAPI.plugin.settings.userHiers.forEach( + h => h[d].forEach( + x => values.add(x) + ) + ) + ); + return Array.from(values); + } + HIERARCHIES.set("down",getHierarchyFields(["down"])); + HIERARCHIES.set("up",getHierarchyFields(["up"])); + HIERARCHIES.set("jump",getHierarchyFields(["prev","next"])); +} else { + HIERARCHIES.set("down",formattingJSON?.breadcrumbs?.down??["down","child","children"]); + HIERARCHIES.set("up",formattingJSON?.breadcrumbs?.up??["up", "parent","parents"]); + HIERARCHIES.set("jump",formattingJSON?.breadcrumbs?.jump??["jump", "jumps", "next", "previous"]); +} + +//------------------------------------------------------- +// Initialization +//------------------------------------------------------- +if(SHOW_CONFIRMATION_PROMPT) { + const result = await utils.inputPrompt( + "This will overwrite the current active drawing", + "type: 'ok' to Continue" + ); + if(result !== "ok") return; +} + +const measureText = (text,fontSize) => { + ea.style.fontSize = fontSize; + return ea.measureText(text); +} + +ea.style.fontFamily = FONT_FAMILY; +const TEXT_SIZE = measureText("m".repeat(MAX_LABEL_LENGTH+3),FONT_SIZE); +const NODE_WIDTH = TEXT_SIZE.width + 3 * PADDING; +const NODE_HEIGHT = 2 * (TEXT_SIZE.height + 2 * PADDING); + +ea.getExcalidrawAPI().updateScene({ + appState: { + viewModeEnabled:true, + theme: "light", + viewBackgroundColor: BG_COLOR + }, + elements:[] +}); + +ea.style.strokeColor = NODE_COLOR; +ea.addText(0,0,"Open a document in another pane and click it to get started.\n\n" + + "For the best experience enable 'Open in adjacent pane'\nin Excalidraw settings " + + "under 'Links and Transclusion'.", {textAlign:"center"}); +await ea.addElementsToView(); +ea.getExcalidrawAPI().zoomToFit(); + +window.excalidrawView = ea.targetView; +window.excalidrawFile = ea.targetView.file; + +ea.targetView.linksAlwaysOpenInANewPane = true; + +new Notice("Brain Graph On"); + +//------------------------------------------------------- +// Supporting functions and classes +//------------------------------------------------------- +const getFilenameFromPath = (path) => { + const mdFile = path.endsWith(".md"); + const filename = path.substring(path.lastIndexOf("/")+1); + return mdFile ? filename.slice(0,-3) : filename; +} + +const getExtension = (path) => { + const lastDot = path.lastIndexOf("."); + if(lastDot === -1) return "md"; + return path.slice(lastDot+1); +} + +const distinct = (data) => Array.from(new Set(data)); + +class Layout { + constructor(spec) { + this.spec = spec; + this.nodes = []; + this.renderedNodes = []; + } + + layout(columns = this.spec.columns) { + const generateLayoutVector = (pattern) => { + const res = []; + let cur = 1; + let state = true; + pattern + .map(p => Math.floor(p)) + .forEach(cnt => { + for(let i=0;i items%2 + ? generateLayoutVector([(columns-items)/2,items,(columns-items)/2]) + : generateLayoutVector([(columns-items)/2,items/2,1,items/2,(columns-items)/2]); + + const sortedNodes = this.nodes.sort((a,b) => a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1) + const itemCount = sortedNodes.length; + if(itemCount === 0) return; + const rowCount = Math.ceil(itemCount / columns); + this.renderedNodes = Array(rowCount).fill().map((_,i) => + (i+1 < rowCount) || (itemCount % columns === 0) + ? Array(columns).fill().map((_,j) => sortedNodes[i*columns+j]) //full row + : getRowLayout(itemCount % columns).map(idx => idx ? sortedNodes[i*columns+idx-1]:null)); + } + + render() { + this.layout(); + const rows = this.renderedNodes.length; + const height = rows * this.spec.rowHeight; + const top = (this.spec.top === null && this.spec.bottom === null) //unconstrained + ? this.spec.origoY - height/2 + : this.spec.top !== null + ? (this.spec.origoY - height/2) < this.spec.top //top constrained + ? this.spec.top + : this.spec.origoY - height/2 + : (this.spec.origoY + height/2) > this.spec.bottom //bottom constrained + ? this.spec.bottom - height + : this.spec.origoY - height/2; + const center00 = { + x: this.spec.origoX - (this.spec.columns === 1 ? 0 : (this.spec.columns-1)/2*this.spec.columnWidth), + y: top + }; + this.renderedNodes.forEach((nodes,row) => + nodes.forEach((node,idx) => { + if(!node) return; + node.setCenter({ + x: center00.x + idx*this.spec.columnWidth, + y: center00.y + row*this.spec.rowHeight + }); + node.render(); + }) + ); + } +} + +class Node { + constructor(spec) { + const dvPage = spec.file ? DataviewAPI?.page(spec.file.path) : null; + const tag = (dvPage?.file?.tags?.values??[]).filter(t=>FORMATTED_TAGS.some(x=>t.startsWith(x)))[0]; + if(tag) { + const format = NODE_FORMATTING[FORMATTED_TAGS.filter(x=>tag.startsWith(x))[0]]; + spec.gateColor = format.gateColor ?? spec.gateColor; + spec.backgroundColor = format.backgroundColor ?? spec.backgroundColor; + spec.nodeColor = format.nodeColor ?? spec.nodeColor; + spec.borderColor = format.borderColor ?? spec.borderColor; + spec.prefix = format.prefix; + } + this.spec = spec; + + const aliases = (spec.file && USE_ALIAS) + ? (dvPage?.file?.aliases?.values??[]) + : []; + const label = (spec.prefix??"") + (aliases.length > 0 + ? aliases[0] + : (spec.file + ? (spec.file.extension === "md" ? spec.file.basename : spec.file.name) + : spec.nodeTitle)); + this.label = label.length > spec.maxLabelLength + ? label.substring(0,spec.maxLabelLength-1) + "..." + : label; + this.labelSize = measureText(this.label, spec.fontSize); + } + + setCenter(center) { + this.center = center; + } + + render() { + ea.style.fontSize = this.spec.fontSize; + ea.style.strokeColor = this.spec.file + ? this.spec.nodeColor + : this.spec.virtualNodeColor; + ea.style.backgroundColor = "transparent"; + this.id = ea.addText( + this.center.x - this.labelSize.width / 2, + this.center.y - this.labelSize.height / 2, + this.label, + { + wrapAt: this.spec.maxLabelLength+5, + textAlign: "center", + box: true, + boxPadding: this.spec.padding + } + ); + const box = ea.getElement(this.id); + box.link = `[[${this.spec.file?.path??this.spec.nodeTitle}]]`; + box.backgroundColor = this.spec.file + ? this.spec.backgroundColor + : this.spec.virtualNodeBGColor; + box.strokeColor = this.spec.borderColor; + box.strokeStyle = this.spec.strokeStyle??"solid"; + + ea.style.strokeColor = this.spec.gateColor; + ea.style.backgroundColor = this.spec.hasJumps ? this.spec.gateColor : "transparent"; + this.jumpGateId = ea.addEllipse( + this.spec.jumpOnLeft + ? this.center.x - this.spec.gateRadius * 2 - this.spec.padding - this.labelSize.width / 2 + : this.center.x + this.spec.padding + this.labelSize.width / 2, + this.center.y - this.spec.gateRadius, + this.spec.gateRadius * 2, + this.spec.gateRadius * 2 + ); + ea.style.backgroundColor = this.spec.hasParents ? this.spec.gateColor : "transparent"; + this.parentGateId = ea.addEllipse( + this.center.x - this.spec.gateRadius - this.spec.gateOffset, + this.center.y - 2 * this.spec.gateRadius - this.spec.padding - this.labelSize.height / 2, + this.spec.gateRadius * 2, + this.spec.gateRadius * 2 + ); + ea.style.backgroundColor = this.spec.hasChildren ? this.spec.gateColor : "transparent"; + this.childGateId = ea.addEllipse( + this.center.x - this.spec.gateRadius + this.spec.gateOffset, + this.center.y + this.spec.padding + this.labelSize.height / 2, + this.spec.gateRadius * 2, + this.spec.gateRadius * 2 + ); + + ea.addToGroup([this.jumpGateId,this.parentGateId,this.childGateId,this.id, box.boundElements[0].id]); + } +} + +const addNodes = (nodesMap, root, nodes, layout, options) => { + nodes.forEach(filePath => { + const node = new Node({ + nodeTitle: getFilenameFromPath(filePath), + file: app.vault.getAbstractFileByPath(filePath), + hasChildren: false, + hasParents: false, + hasJumps: false, + ...options + }); + nodesMap.set(filePath,node); + layout.nodes.push(node); + }); +} + +//------------------------------------------------------- +// Breadcrumbs workaround to handle full filepath +//------------------------------------------------------- +const getPathOrSelf = (link,host) => { + return (app.metadataCache.getFirstLinkpathDest(link,host)?.path)??link; +} + +const readDVField = (field,file) => { + const res = new Set(); + if(field.values) { + field.values.forEach(l=>{ + if(l.type === "file") res.add(getPathOrSelf(l.path,file.path)); + }); + return Array.from(res); + } + if(field.path) { + return [getPathOrSelf(field.path,file.path)]; + } + const m = field.matchAll(/[^[]*\[\[([^#\]\|]*)[^\]]*]]/g); + while(!(r=m.next()).done) { + if(r.value[1]) { + res.add(getPathOrSelf(r.value[1],file.path)); + } + } + return Array.from(res); +} + +const getDVFieldLinks = (page,dir,retSet=false) => { + const fields = HIERARCHIES.get(dir); + const links = new Set(); + const processed = new Set(); + fields.forEach(f => { + if(page[f] && !processed.has(f)) { + processed.add(f); + readDVField(page[f],page.file).forEach(l=>links.add(l)) + }; + }); + return retSet ? links : Array.from(links); +} + +//------------------------------------------------------- +// Event handler +//------------------------------------------------------- +window.brainGraphEventHandler = async (leaf) => { + //sleep for 100ms to give time for leaf.view.file to be set + await new Promise((resolve) => setTimeout(resolve, 100)); + + //------------------------------------------------------- + //terminate event handler if view no longer exists or file has changed + if(!window.excalidrawView?.file || window.excalidrawView.file.path !== window.excalidrawFile?.path) { + removeEventHandler(); + return; + } + + if(!leaf?.view?.file) return; + const rootFile = leaf.view.file; + + if (rootFile.path === window.excalidrawFile.path) return; //brainview drawing is active + + if(window.lastfilePath && window.lastfilePath === rootFile.path) return; //don't reload the file if it has not changed + window.lastfilePath = rootFile.path; + + //------------------------------------------------------- + //I must reinitialize ea because in the event handler the script engine ea will no longer be available + ea = ExcalidrawAutomate; + ea.reset(); + ea.setView(window.excalidrawView); + ea.style.fontFamily = FONT_FAMILY; + ea.style.roughness = STROKE_ROUGHNESS; + ea.style.strokeSharpness = STROKE_SHARPNESS; + ea.getExcalidrawAPI().updateScene({elements:[]}); + ea.style.verticalAlign = "middle"; + ea.style.strokeSharpness = "round"; + ea.style.fillStyle = "solid"; + + //------------------------------------------------------- + //build list of nodes + const dvPage = DataviewAPI.page(rootFile.path); //workaround because + const parentsSet = dvPage + ? getDVFieldLinks(dvPage,"up",true) + : new Set(); + parentsSet.delete(rootFile.path); + + const childrenSet = dvPage + ? getDVFieldLinks(dvPage,"down",true) + : new Set(); + childrenSet.delete(rootFile.path); + + const jumpsSet = dvPage + ? getDVFieldLinks(dvPage,"jump",true) + : new Set(); + jumpsSet.delete(rootFile.path); + + let backlinksSet = new Set( + Object.keys((app.metadataCache.getBacklinksForFile(rootFile)?.data)??{}) + .map(l=>app.metadataCache.getFirstLinkpathDest(l,rootFile.path)?.path??l) + ); + + backlinksSet.forEach(l=>{ + const page = DataviewAPI.page(l); + if(page) { + if(getDVFieldLinks(page,"down",true).has(rootFile.path)) { + parentsSet.add(l); + backlinksSet.delete(l); + return; + } + if(getDVFieldLinks(page,"up",true).has(rootFile.path)) { + childrenSet.add(l); + backlinksSet.delete(l); + return; + } + if(getDVFieldLinks(page,"jump", true).has(rootFile.path)) { + jumpsSet.add(l); + backlinksSet.delete(l); + return; + } + } + }) + + const parents = Array.from(parentsSet).slice(0,MAX_ITEMS); + const children = Array.from(childrenSet).slice(0,MAX_ITEMS); + const jumps = Array.from(jumpsSet).slice(0,MAX_ITEMS); + + //adding links from the document, not explicitly declared as a breadcrumb + const forwardLinks = INCLUDE_OBSIDIAN_LINKS + ? distinct(app.metadataCache + .getLinks()[rootFile.path]?.map( + l=>app.metadataCache.getFirstLinkpathDest(l.link.match(/[^#]*/)[0],rootFile.path)??l.link.match(/[^#]*/)[0] + ).filter(f=>f && (!HIDE_ATTACHMENTS || (f.extension??getExtension(f)) === "md")) + .map(f=>f.path??f) + .filter(l=>!parentsSet.has(l) && !childrenSet.has(l) && !jumpsSet.has(l) && l!==rootFile.path && !backlinksSet.has(l)) + .slice(0,Math.max(MAX_ITEMS-children.length)) + ) + : []; + const forwardlinksSet = new Set(forwardLinks); + + const backLinks = INCLUDE_OBSIDIAN_LINKS + ? Array.from(backlinksSet) + .filter(l=>!parentsSet.has(l) && !childrenSet.has(l) && + !jumpsSet.has(l) && l!==rootFile.path && !forwardlinksSet.has(l)) + .slice(0,Math.max(MAX_ITEMS-parents.length)) + : []; + backlinksSet = new Set(backLinks); + + const sharedParents = INCLUDE_OBSIDIAN_LINKS + ? backLinks.concat(parents) + : parents; + + const siblings = distinct( + sharedParents.map(p=>{ + const page = DataviewAPI.page(p); + return page + ? getDVFieldLinks(page,"down") + .filter(l=>!parentsSet.has(l) && !childrenSet.has(l) && + !jumpsSet.has(l) && l!==rootFile.path && + !forwardlinksSet.has(l) && !backlinksSet.has(l)) + : []; + }).flat() + ).slice(0,MAX_ITEMS); + const siblingsSet = new Set(siblings); + + const inverseInferredSiblingsMap = new Map(); + const inferredSiblings = INCLUDE_OBSIDIAN_LINKS + ? distinct( + sharedParents.map(p=>{ + f = app.vault.getAbstractFileByPath(p); //app.metadataCache.getFirstLinkpathDest(p,rootFile.path); + if(!f) { + return []; + } + const res = Object.keys((app.metadataCache.getBacklinksForFile(f)?.data)??{}) + .map(l=>app.metadataCache.getFirstLinkpathDest(l,rootFile.path)?.path??l) + .filter(l=>!parentsSet.has(l) && !childrenSet.has(l) && !jumpsSet.has(l) && + l!==rootFile.path && !forwardlinksSet.has(l) && !backlinksSet.has(l) && !siblingsSet.has(l)); + res.forEach(r=>inverseInferredSiblingsMap.set(r,p)); + return res; + }).flat() + ).slice(0,Math.max(MAX_ITEMS-siblings.length)) + : []; + const inferredSiblingsSet = new Set(inferredSiblings); + + //------------------------------------------------------- + // Generate layout and nodes + const nodesMap = new Map(); + const linksMap = new Map(); + + const lCenter = new Layout({ + origoX: 0, + origoY: 0, + top: null, + bottom: null, + columns: 1, + columnWidth: NODE_WIDTH, + rowHeight: NODE_HEIGHT + }); + + const manyChildren = (children.length + forwardLinks.length) >10; + const manySiblings = (siblings.length + inferredSiblings.length) > 10; + const singleParent = (parents.length + backLinks.length) <= 1 + + const lChildren = new Layout({ + origoX: 0, + origoY: 2.5 * NODE_HEIGHT, + top: 2.5 * NODE_HEIGHT - NODE_HEIGHT/2, + bottom: null, + columns: manyChildren ? 5 : 3, + columnWidth: NODE_WIDTH, + rowHeight: NODE_HEIGHT + }); + + const lJumps = new Layout({ + origoX: (manyChildren ? -3 : -2) * NODE_WIDTH, + origoY: 0, + top: null, + bottom: null, + columns: 1, + columnWidth: NODE_WIDTH, + rowHeight: NODE_HEIGHT + }); + + const lParents = new Layout({ + origoX: 0, + origoY: -2.5 * NODE_HEIGHT, + top: null, + bottom: -2.5 * NODE_HEIGHT + NODE_HEIGHT/2, + columns: 3, + columnWidth: NODE_WIDTH, + rowHeight: NODE_HEIGHT + }); + + const lSiblings = new Layout({ + origoX: NODE_WIDTH * ((singleParent ? 0 : 1) + (manySiblings ? 2 : 1)), + origoY: -2.5 * NODE_HEIGHT, + top: null, + bottom: NODE_HEIGHT, + columns: (manySiblings ? 3 : 1), + columnWidth: NODE_WIDTH, + rowHeight: NODE_HEIGHT + }) + + const rootNode = new Node({ + file: rootFile, + hasChildren: false, + hasParents: false, + hasJumps: false, + fontSize: CENTRAL_FONT_SIZE, + jumpOnLeft: true, + maxLabelLength: 2*MAX_LABEL_LENGTH, + gateRadius: GATE_RADIUS, + gateOffset: GATE_OFFSET, + padding: PADDING, + nodeColor: CENTRAL_NODE_COLOR, + gateColor: GATE_COLOR, + borderColor: CENTRAL_NODE_COLOR, + backgroundColor: CENTRAL_NODE_BG_COLOR, + virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR + }); + nodesMap.set(rootFile.path,rootNode); + lCenter.nodes.push(rootNode); + + addNodes( + nodesMap, + rootFile, + parents, + lParents, + { + fontSize: FONT_SIZE, + jumpOnLeft: true, + maxLabelLength: MAX_LABEL_LENGTH, + gateRadius: GATE_RADIUS, + gateOffset: GATE_OFFSET, + padding: PADDING, + nodeColor: NODE_COLOR, + gateColor: GATE_COLOR, + borderColor: "transparent", + backgroundColor: NODE_BG_COLOR, + virtualNodeColor: VIRTUAL_NODE_COLOR, + virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR + } + ); + + addNodes( + nodesMap, + rootFile, + backLinks, + lParents, + { + fontSize: FONT_SIZE, + jumpOnLeft: true, + maxLabelLength: MAX_LABEL_LENGTH, + gateRadius: GATE_RADIUS, + gateOffset: GATE_OFFSET, + padding: PADDING, + nodeColor: OBSIDIAN_NODE_COLOR, + gateColor: GATE_COLOR, + borderColor: OBSIDIAN_NODE_BG_COLOR, + strokeStyle: "dotted", + strokeWidth: 3, + backgroundColor: OBSIDIAN_NODE_BG_COLOR, + virtualNodeColor: VIRTUAL_NODE_COLOR, + virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR + } + ); + + addNodes( + nodesMap, + rootFile, + children, + lChildren, + { + fontSize: FONT_SIZE, + jumpOnLeft: true, + maxLabelLength: MAX_LABEL_LENGTH, + gateRadius: GATE_RADIUS, + gateOffset: GATE_OFFSET, + padding: PADDING, + nodeColor: NODE_COLOR, + gateColor: GATE_COLOR, + borderColor: "transparent", + backgroundColor: NODE_BG_COLOR, + virtualNodeColor: VIRTUAL_NODE_COLOR, + virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR + } + ); + + addNodes( + nodesMap, + rootFile, + forwardLinks, + lChildren, + { + fontSize: FONT_SIZE, + jumpOnLeft: true, + maxLabelLength: MAX_LABEL_LENGTH, + gateRadius: GATE_RADIUS, + gateOffset: GATE_OFFSET, + padding: PADDING, + nodeColor: OBSIDIAN_NODE_COLOR, + gateColor: GATE_COLOR, + borderColor: OBSIDIAN_NODE_BG_COLOR, + strokeStyle: "dotted", + strokeWidth: 3, + backgroundColor: OBSIDIAN_NODE_BG_COLOR, + virtualNodeColor: VIRTUAL_NODE_COLOR, + virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR + } + ); + + addNodes( + nodesMap, + rootFile, + jumps, + lJumps, + { + fontSize: FONT_SIZE, + jumpOnLeft: false, + maxLabelLength: MAX_LABEL_LENGTH, + gateRadius: GATE_RADIUS, + gateOffset: GATE_OFFSET, + padding: PADDING, + nodeColor: NODE_COLOR, + gateColor: GATE_COLOR, + borderColor: "transparent", + backgroundColor: NODE_BG_COLOR, + virtualNodeColor: VIRTUAL_NODE_COLOR, + virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR + } + ); + + addNodes( + nodesMap, + rootFile, + siblings, + lSiblings, + { + fontSize: DISTANT_FONT_SIZE, + jumpOnLeft: true, + maxLabelLength: MAX_LABEL_LENGTH, + gateRadius: GATE_RADIUS, + gateOffset: GATE_OFFSET, + padding: PADDING, + nodeColor: NODE_COLOR, + gateColor: GATE_COLOR, + borderColor: "transparent", + backgroundColor: NODE_BG_COLOR, + virtualNodeColor: VIRTUAL_NODE_COLOR, + virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR + } + ); + + addNodes( + nodesMap, + rootFile, + inferredSiblings, + lSiblings, + { + fontSize: DISTANT_FONT_SIZE, + jumpOnLeft: true, + maxLabelLength: MAX_LABEL_LENGTH, + gateRadius: GATE_RADIUS, + gateOffset: GATE_OFFSET, + padding: PADDING, + nodeColor: OBSIDIAN_NODE_COLOR, + gateColor: GATE_COLOR, + borderColor: OBSIDIAN_NODE_BG_COLOR, + strokeStyle: "dotted", + strokeWidth: 3, + backgroundColor: OBSIDIAN_NODE_BG_COLOR, + virtualNodeColor: VIRTUAL_NODE_COLOR, + virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR + } + ); + + //------------------------------------------------------- + // Generate links for all displayed nodes + const separator = "?"; //any character disallowed in filenames + Array.from(nodesMap.keys()).forEach(filePath => { + const node = nodesMap.get(filePath); + const dvPage = DataviewAPI.page(filePath); + + //------------------------------------------------------- + // Add links originating from node + const parent = forwardlinksSet.has(filePath) + ? rootFile.path + : inverseInferredSiblingsMap.get(filePath); + const parents = dvPage + ? (parent ? getDVFieldLinks(dvPage,"up",true).add(parent) : getDVFieldLinks(dvPage,"up",true)) + : (parent ? new Set([parent]) : new Set()); + + const child = backlinksSet.has(filePath) ? rootFile.path : null + const children = dvPage + ? (child ? getDVFieldLinks(dvPage,"down", true).add(child) : getDVFieldLinks(dvPage,"down", true)) + : (child ? new Set([child]) : new Set()); + + const jumps = dvPage + ? getDVFieldLinks(dvPage,"jump", true) + : new Set(); + + //siblings are left out in this case on purpose + + const addLinks = (nodes,relation) => nodes.forEach(n => { + if(!nodesMap.has(n)) return; + if(linksMap.has(filePath + separator + n)) return; + linksMap.set(filePath + separator + n,relation); + linksMap.set(n + separator + filePath,null); + }); + + addLinks(parents,["parent","child"]); + addLinks(children, ["child","parent"]); + addLinks(jumps, ["jump","jump"]); + + //------------------------------------------------------- + //Set gate fill + //- if node has outgoing links + node.spec.hasChildren = node.spec.hasChildren || children.size>0; + node.spec.hasParents = node.spec.hasParents || parents.size>0; + node.spec.hasJumps = node.spec.hasJumps || jumps.size>0; + + //- of the nodes on the other end of outgoing links + // this is required on top of backlinks, to handle + // nodes for referenced but not yet created pages + parents.forEach(p=>{ + const n = nodesMap.get(p); + if(n) n.spec.hasChildren = true; + }); + + children.forEach(p=>{ + const n = nodesMap.get(p); + if(n) n.spec.hasParents = true; + }); + + jumps.forEach(p=>{ + const n = nodesMap.get(p); + if(n) n.spec.hasJumps = true; + }); + + //- if node has an incoming link from a node outside the visible graph + if(node.spec.file && !(node.spec.hasParents && node.spec.hasChildren && node.hasJumps)) { + const nodeBacklinks = new Set( + Object.keys((app.metadataCache.getBacklinksForFile(node.spec.file)?.data)??{}) + .map(l=>app.metadataCache.getFirstLinkpathDest(l,filePath)?.path??l) + ); + + nodeBacklinks.forEach(l=>{ + if(node.spec.hasParents && node.spec.hasChildren && node.hasJumps) return; + const page = DataviewAPI.page(l); + if(page) { + if(getDVFieldLinks(page,"down",true).has(filePath)) { + node.spec.hasParents = true; + return; + } + if(getDVFieldLinks(page,"up",true).has(filePath)) { + node.spec.hasChildren = true; + return; + } + if(getDVFieldLinks(page,"jump",true).has(filePath)) { + node.spec.hasJumps = true; + return; + } + if(INCLUDE_OBSIDIAN_LINKS) { + node.spec.hasParents = true; + return; + } + } + }); + } + + if(!node.spec.hasChildren && INCLUDE_OBSIDIAN_LINKS && node.spec.file) { + const forwardLinks = app.metadataCache + .getLinks()[filePath]?.map( + l=>app.metadataCache.getFirstLinkpathDest(l.link.match(/[^#]*/)[0],rootFile.path)??l.link.match(/[^#]*/)[0] + ).filter(f=>f && (!HIDE_ATTACHMENTS || (f.extension??getExtension(f)) === "md")) + .map(f=>f.path??f) + .filter(l=>!parents.has(l) && !children.has(l) && !jumps.has(l) && l!==rootFile.path); + if(forwardLinks.length > 0) { + node.spec.hasChildren = true; + } + } + }); + + //------------------------------------------------------- + // Render + lCenter.render(); + lParents.render(); + lChildren.render(); + lJumps.render(); + lSiblings.render(); + + const getGate = (key,value) => (value === "parent") + ? nodesMap.get(key).parentGateId + : (value === "child") + ? nodesMap.get(key).childGateId + : nodesMap.get(key).jumpGateId; + + ea.style.strokeColor = LINK_COLOR; + for([key,value] of linksMap) { + if(value) { + const k=key.split(separator); + const gate1 = getGate(k[0],value[0]); + const gate2 = getGate(k[1],value[1]); + ea.connectObjects(gate1, null, gate2, null, { startArrowHead: null, endArrowHead: null }); + } + } + + elements = ea.getElements(); + ea.getExcalidrawAPI().updateScene({ + elements: elements.filter( + el=>el.type==="arrow" + ).concat(elements.filter(el=>el.type!=="arrow")) + }) + ea.getExcalidrawAPI().zoomToFit(); +} + +//------------------------------------------------------- +// Initiate event listner and trigger plex if file is open +//------------------------------------------------------- +app.workspace.on(EVENT, window.brainGraphEventHandler); + +ea.targetView.semaphores.saving = true; //disable saving by setting this Excalidraw flag (not published API) + +const mdLeaf = app.workspace.getLeavesOfType("markdown"); +if(mdLeaf.length>0) { + window.brainGraphEventHandler(mdLeaf[0]); + return; +} + +const mdExcalidrawLeaf = app.workspace.getLeavesOfType("excalidraw").filter(l=>l!==ea.targetView.leaf); +if(mdExcalidrawLeaf.length>0) { + window.brainGraphEventHandler(mdExcalidrawLeaf[0]); + return; +} + +const mdImageLeaf = app.workspace.getLeavesOfType("image"); +if(mdImageLeaf.length>0) { + window.brainGraphEventHandler(mdImageLeaf[0]); + return; } \ No newline at end of file diff --git a/ea-scripts/TheBrain-navigation.svg b/ea-scripts/Archive/TheBrain-navigation.svg similarity index 100% rename from ea-scripts/TheBrain-navigation.svg rename to ea-scripts/Archive/TheBrain-navigation.svg diff --git a/ea-scripts/Transfer TextElements to Excalidraw markdown metadata.md b/ea-scripts/Archive/Transfer TextElements to Excalidraw markdown metadata.md similarity index 97% rename from ea-scripts/Transfer TextElements to Excalidraw markdown metadata.md rename to ea-scripts/Archive/Transfer TextElements to Excalidraw markdown metadata.md index fbdf12d..8ac6993 100644 --- a/ea-scripts/Transfer TextElements to Excalidraw markdown metadata.md +++ b/ea-scripts/Archive/Transfer TextElements to Excalidraw markdown metadata.md @@ -1,44 +1,44 @@ -/* -![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg) - -Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian. - -![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-to-metadata.jpg) - -The script will delete the selected text elements from the canvas and will copy the text from these text elements into the Excalidraw markdown file as metadata. This means, that the text will no longer be visible in the drawing, however you will be able to search for the text in Obsidian and find the drawing containing this image. - -See ScriptEngine documentation for more details: -https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html - -```javascript -*/ -//get text elements - -const textElements = ea.getViewSelectedElements().filter((el)=>el.type==="text"); - -if(textElements.length===0) { - notice("No text elements were selected") - return; -} - -metadata = "# Metadata\n" + textElements - .map((el)=>el.rawText.replaceAll(/%|\^/g,"_")) //cleaning these characters for safety, might not be needed - .join("/n") + "\n"; - -ea.deleteViewElements(textElements); -await ea.targetView.save(); -data = await app.vault.read(ea.targetView.file); -splitAfterFrontmatter = data.split(/(^---[\w\W]*?---\n)/); -if(splitAfterFrontmatter.length !== 3) { - notice("Error locating frontmatter in markdown file"); - console.log({file:ea.targetView.file}); - return; -} -newData = splitAfterFrontmatter[1]+metadata+splitAfterFrontmatter[2] -await app.vault.modify(ea.targetView.file,newData); - -//utility function -function notice(message) { - new Notice(message); - console.log(message); -} +/* +![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg) + +Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian. + +![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-to-metadata.jpg) + +The script will delete the selected text elements from the canvas and will copy the text from these text elements into the Excalidraw markdown file as metadata. This means, that the text will no longer be visible in the drawing, however you will be able to search for the text in Obsidian and find the drawing containing this image. + +See ScriptEngine documentation for more details: +https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html + +```javascript +*/ +//get text elements + +const textElements = ea.getViewSelectedElements().filter((el)=>el.type==="text"); + +if(textElements.length===0) { + notice("No text elements were selected") + return; +} + +metadata = "# Metadata\n" + textElements + .map((el)=>el.rawText.replaceAll(/%|\^/g,"_")) //cleaning these characters for safety, might not be needed + .join("/n") + "\n"; + +ea.deleteViewElements(textElements); +await ea.targetView.save(); +data = await app.vault.read(ea.targetView.file); +splitAfterFrontmatter = data.split(/(^---[\w\W]*?---\n)/); +if(splitAfterFrontmatter.length !== 3) { + notice("Error locating frontmatter in markdown file"); + console.log({file:ea.targetView.file}); + return; +} +newData = splitAfterFrontmatter[1]+metadata+splitAfterFrontmatter[2] +await app.vault.modify(ea.targetView.file,newData); + +//utility function +function notice(message) { + new Notice(message); + console.log(message); +} diff --git a/ea-scripts/Transfer TextElements to Excalidraw markdown metadata.svg b/ea-scripts/Archive/Transfer TextElements to Excalidraw markdown metadata.svg similarity index 100% rename from ea-scripts/Transfer TextElements to Excalidraw markdown metadata.svg rename to ea-scripts/Archive/Transfer TextElements to Excalidraw markdown metadata.svg diff --git a/ea-scripts/index.md b/ea-scripts/Archive/index.md similarity index 99% rename from ea-scripts/index.md rename to ea-scripts/Archive/index.md index 6b5f424..aa65c18 100644 --- a/ea-scripts/index.md +++ b/ea-scripts/Archive/index.md @@ -1,348 +1,348 @@ -If you are enjoying the Excalidraw plugin then please support my work and enthusiasm by buying me a coffee on [https://ko-fi/zsolt](https://ko-fi.com/zsolt). - -[](https://ko-fi.com/zsolt) - ---- - -Jump ahead to the [[#List of available scripts]] - -# Introducing Excalidraw Automate Script Engine - - -Script Engine scripts are installed in the `Downloaded` subfolder of the `Excalidraw Automate script folder` specified in plugin settings. - -In the `Command Palette` installed scripts are prefixed with `Downloaded/`, thus you can always know if you are executing a local script of your own, or one that you have downloaded from GitHub. - -## Attention developers and hobby hackers - -If you want to modify scripts, I recommend moving them to the `Excalidraw Automate script folder` or a different subfolder under the script folder. Scripts in the `Downloaded` folder will be overwritten when you click the `Update this script` button. Note also, that at this time, I do not check if the script file has been updated on GitHub, thus the `Update this script` button is always visible once you have installed a script, not only when an update is availble (hope to build this feature in the future). - -I would love to include your contribution in the script library. If you have a script of your own that you would like to share with the community, please open a [PR](https://github.com/zsviczian/obsidian-excalidraw-plugin/pulls) on GitHub. Be sure to include the following in your pull request -- The [script file](https://github.com/zsviczian/obsidian-excalidraw-plugin/tree/master/ea-scripts) with a self explanetory name. The name of the file will be the name of the script in the Command Palette. -- An [image](https://github.com/zsviczian/obsidian-excalidraw-plugin/tree/master/images) explaining the scripts purpose. Remember a picture speaks thousand words! -- An update to this file [ea-scripts/index.md](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/index.md) - ---- - -# List of available scripts -- [[#Add Connector Point]] -- [[#Add Link to Existing File and Open]] -- [[#Add Link to New Page and Open]] -- [[#Add Next Step in Process]] -- [[#Box Each Selected Groups]] -- [[#Box Selected Elements]] -- [[#Change shape of selected elements]] -- [[#Connect elements]] -- [[#Convert freedraw to line]] -- [[#Convert selected text elements to sticky notes]] -- [[#Convert text to link with folder and alias]] -- [[#Copy Selected Element Styles to Global]] -- [[#Create new markdown file and embed into active drawing]] -- [[#Darken background color]] -- [[#Elbow connectors]] -- [[#Expand rectangles horizontally keep text centered]] -- [[#Expand rectangles horizontally]] -- [[#Expand rectangles vertically keep text centered]] -- [[#Expand rectangles vertically]] -- [[#Fixed horizontal distance between centers]] -- [[#Fixed inner distance]] -- [[#Fixed spacing]] -- [[#Fixed vertical distance between centers]] -- [[#Fixed vertical distance]] -- [[#Lighten background color]] -- [[Mindmap connector]] -- [[#Modify background color opacity]] -- [[#Normalize Selected Arrows]] -- [[#OCR - Optical Character Recognition]] -- [[#Organic Line]] -- [[#Repeat Elements]] -- [[#Reverse arrows]] -- [[#Scribble Helper]] -- [[#Select Elements of Type]] -- [[#Set background color of unclosed line object by adding a shadow clone]] -- [[#Set Dimensions]] -- [[#Set Font Family]] -- [[#Set Grid]] -- [[#Set Link Alias]] -- [[#Set Stroke Width of Selected Elements]] -- [[#Set Text Alignment]] -- [[#Split text by lines]] -- [[#Toggle Fullscreen on Mobile]] -- [[#Transfer TextElements to Excalidraw markdown metadata]] -- [[#Zoom to Fit Selected Elements]] - -## Add Connector Point -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Connector%20Point.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionThis script will add a small circle to the top left of each text element in the selection and add the text and the "connector point" to a group. You can use the connector points to link text elements with an arrow (in for example a Wardley Map).
- -## Add Link to Existing File and Open -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Link%20to%20Existing%20File%20and%20Open.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionPrompts for a file from the vault. Adds a link to the selected element pointing to the selected file. You can control in settings to open the file in the current active pane or an adjacent pane.
- -## Add Link to New Page and Open -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Link%20to%20New%20Page%20and%20Open.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionPrompts for filename. Offers option to create and open a new Markdown or Excalidraw document. Adds link pointing to the new file, to the selected objects in the drawing. You can control in settings to open the file in the current active pane or an adjacent pane.
- -## Add Next Step in Process -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Next%20Step%20in%20Process.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionThis script will prompt you for the title of the process step, then will create a stick note with the text. If an element is selected then the script will connect this new step with an arrow to the previous step (the selected element). If no element is selected, then the script assumes this is the first step in the process and will only output the sticky note with the text that was entered.
- -## Box Each Selected Groups -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Box%20Each%20Selected%20Groups.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script will add encapsulating boxes around each of the currently selected groups in Excalidraw.
- -## Box Selected Elements -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Box%20Selected%20Elements.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionThis script will add an encapsulating box around the currently selected elements in Excalidraw.
- -## Change shape of selected elements -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Change%20shape%20of%20selected%20elements.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionThe script allows you to change the shape of selected Rectangles, Diamonds and Ellipses.
- -## Connect elements -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Connect%20elements.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionThis script will connect two objects with an arrow. If either of the objects are a set of grouped elements (e.g. a text element grouped with an encapsulating rectangle), the script will identify these groups, and connect the arrow to the largest object in the group (assuming you want to connect the arrow to the box around the text element).
- -## Convert freedraw to line -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20freedraw%20to%20line.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionConvert selected freedraw objects into editable lines. This will allow you to adjust your drawings by dragging line points and will also allow you to select shape fill in case of enclosed lines. You can adjust conversion point density in settings.
- -## Convert selected text elements to sticky notes -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20selected%20text%20elements%20to%20sticky%20notes.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionConverts selected plain text elements to sticky notes with transparent background and transparent stroke color (default setting, can be changed in plugin settings). Essentially converts text element into a wrappable format.
- -## Convert text to link with folder and alias -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20text%20to%20link%20with%20folder%20and%20alias.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionConverts text elements to links pointing to a file in a selected folder and with the alias set as the original text. The script will prompt the user to select an existing folder from the vault.
original text - [[selected folder/original text|original text]]
- -## Copy Selected Element Styles to Global -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Copy%20Selected%20Element%20Styles%20to%20Global.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script will copy styles of any selected element into Excalidraw's global styles.
- -## Create new markdown file and embed into active drawing -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Create%20new%20markdown%20file%20and%20embed%20into%20active%20drawing.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionThe script will prompt you for a filename, then create a new markdown document with the file name provided, open the new markdown document in an adjacent pane, and embed the markdown document into the active Excalidraw drawing.
- -## Darken background color -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Darken%20background%20color.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script darkens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect. In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.
- -## Elbow connectors -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Elbow%20connectors.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script converts the selected connectors to elbows.
- -## Expand rectangles horizontally keep text centered -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20horizontally%20keep%20text%20centered.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script expands the width of the selected rectangles until they are all the same width and keep the text centered.
- -## Expand rectangles horizontally -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20horizontally.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script expands the width of the selected rectangles until they are all the same width.
- -## Expand rectangles vertically keep text centered -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20vertically%20keep%20text%20centered.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script expands the height of the selected rectangles until they are all the same height and keep the text centered.
- -## Expand rectangles vertically -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20vertically.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script expands the height of the selected rectangles until they are all the same height.
- -## Fixed horizontal distance between centers -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20horizontal%20distance%20between%20centers.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script arranges the selected elements horizontally with a fixed center spacing.
- -## Fixed inner distance -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20inner%20distance.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script arranges selected elements and groups with a fixed inner distance.
- -## Fixed spacing -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20spacing.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThe script arranges the selected elements horizontally with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.
- -## Fixed vertical distance between centers -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20vertical%20distance%20between%20centers.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script arranges the selected elements vertically with a fixed center spacing.
- -## Fixed vertical distance -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20vertical%20distance.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThe script arranges the selected elements vertically with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.
- -## Lighten background color -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Lighten%20background%20color.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script lightens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect.In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.
- -## Mindmap connector -```excalidraw-script-install -https://github.com/xllowl/obsidian-excalidraw-plugin/blob/master/ea-scripts/Mindmap%20connector.md -``` -
Author@xllowl
SourceFile on GitHub
DescriptionThis script creates mindmap like lines(only right side available). The line will starts according to the creation time of the elements. So you may need to create the header element first.
- -## Modify background color opacity -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Modify%20background%20color%20opacity.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script changes the opacity of the background color of the selected boxes. The default background color in Excalidraw is so dark that the text is hard to read. You can lighten the color a bit by setting transparency. And you can tweak the transparency over and over again until you're happy with it. Although excalidraw has the opacity option in its native property Settings, it also changes the transparency of the border. Use this script to change only the opacity of the background color without affecting the border.
- -## Normalize Selected Arrows -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Normalize%20Selected%20Arrows.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.
- -## OCR - Optical Character Recognition -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/OCR%20-%20Optical%20Character%20Recognition.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionREQUIRES EXCALIDRAW 1.5.15
The script will 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and 2) will add the text to your drawing as a text element.
âš  Note that you will need to manually paste your token into the script after the first run! âš 

- -## Organic Line -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Organic%20Line.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionConverts selected freedraw lines such that pencil pressure will decrease from maximum to minimum from the beginning of the line to its end. The resulting line is placed at the back of the layers, under all other items. Helpful when drawing organic mindmaps.
- -## Repeat Elements -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Repeat%20Elements.md -``` -
Author@1-2-3
SourceFile on GitHub
DescriptionThis script will detect the difference between 2 selected elements, including position, size, angle, stroke and background color, and create several elements that repeat these differences based on the number of repetitions entered by the user.
- -## Reverse arrows -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Reverse%20arrows.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionReverse the direction of **arrows** within the scope of selected elements.
- -## Scribble Helper -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Scribble%20Helper.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptioniOS scribble helper for better handwriting experience with text elements. If no elements are selected then the creates a text element at pointer position and you can use the edit box to modify the text with scribble. If a text element is selected then opens the input prompt where you can modify this text with scribble.
- -## Select Elements of Type -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Select%20Elements%20of%20Type.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionPrompts you with a list of the different element types in the active image. Only elements of the selected type will be selected on the canvas. If nothing is selected when running the script, then the script will process all the elements on the canvas. If some elements are selected when the script is executed, then the script will only process the selected elements.
The script is useful when, for example, you want to bring to front all the arrows, or want to change the color of all the text elements, etc.
- -## Set background color of unclosed line object by adding a shadow clone -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionUse this script to set the background color of unclosed (i.e. open) line objects by creating a clone of the object. The script will set the stroke color of the clone to transparent and will add a straight line to close the object. Use settings to define the default background color, the fill style, and the strokeWidth of the clone. By default the clone will be grouped with the original object, you can disable this also in settings.
- -## Set Dimensions -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Dimensions.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionCurrently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.
- -## Set Font Family -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Font%20Family.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionSets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.
- -## Set Grid -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Grid.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionThe default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.
- -## Set Link Alias -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Link%20Alias.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionIterates all of the links in the selected TextElements and prompts the user to set or modify the alias for each link found.
- -## Set Stroke Width of Selected Elements -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Stroke%20Width%20of%20Selected%20Elements.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionThis script will set the stroke width of selected elements. This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.
- -## Set Text Alignment -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Text%20Alignment.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionSets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.
- -## Split text by lines -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Split%20text%20by%20lines.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionSplit lines of text into separate text elements for easier reorganization
- -## TheBrain-navigation -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/TheBrain-navigation.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionAn Excalidraw based graph user interface for your Vault. Requires the Breadcrumbs plugin to be installed and configured as well. Generates a user interface similar to that of TheBrain. Watch this introduction to this script on YouTube.
- -## Toggle Fullscreen on Mobile -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Toggle%20Fullscreen%20on%20Mobile.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionHides Obsidian workspace leaf padding and header (based on option in settings, default is "hide header" = false) which will take Excalidraw to full screen. âš  Note that if the header is not visible, it will be very difficult to invoke the command palette to end full screen. Only hide the header if you have a keyboard or you've practiced opening command palette!
- -## Transfer TextElements to Excalidraw markdown metadata -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionThe script will delete the selected text elements from the canvas and will copy the text from these text elements into the Excalidraw markdown file as metadata. This means, that the text will no longer be visible in the drawing, however you will be able to search for the text in Obsidian and find the drawing containing this image.
- -## Zoom to Fit Selected Elements -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionSimilar to Excalidraw standard SHIFT+2 feature: Zoom to fit selected elements, but with the ability to zoom to 1000%. Inspiration: [#272](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/272)
+If you are enjoying the Excalidraw plugin then please support my work and enthusiasm by buying me a coffee on [https://ko-fi/zsolt](https://ko-fi.com/zsolt). + +[](https://ko-fi.com/zsolt) + +--- + +Jump ahead to the [[#List of available scripts]] + +# Introducing Excalidraw Automate Script Engine + + +Script Engine scripts are installed in the `Downloaded` subfolder of the `Excalidraw Automate script folder` specified in plugin settings. + +In the `Command Palette` installed scripts are prefixed with `Downloaded/`, thus you can always know if you are executing a local script of your own, or one that you have downloaded from GitHub. + +## Attention developers and hobby hackers + +If you want to modify scripts, I recommend moving them to the `Excalidraw Automate script folder` or a different subfolder under the script folder. Scripts in the `Downloaded` folder will be overwritten when you click the `Update this script` button. Note also, that at this time, I do not check if the script file has been updated on GitHub, thus the `Update this script` button is always visible once you have installed a script, not only when an update is availble (hope to build this feature in the future). + +I would love to include your contribution in the script library. If you have a script of your own that you would like to share with the community, please open a [PR](https://github.com/zsviczian/obsidian-excalidraw-plugin/pulls) on GitHub. Be sure to include the following in your pull request +- The [script file](https://github.com/zsviczian/obsidian-excalidraw-plugin/tree/master/ea-scripts) with a self explanetory name. The name of the file will be the name of the script in the Command Palette. +- An [image](https://github.com/zsviczian/obsidian-excalidraw-plugin/tree/master/images) explaining the scripts purpose. Remember a picture speaks thousand words! +- An update to this file [ea-scripts/index.md](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/index.md) + +--- + +# List of available scripts +- [[#Add Connector Point]] +- [[#Add Link to Existing File and Open]] +- [[#Add Link to New Page and Open]] +- [[#Add Next Step in Process]] +- [[#Box Each Selected Groups]] +- [[#Box Selected Elements]] +- [[#Change shape of selected elements]] +- [[#Connect elements]] +- [[#Convert freedraw to line]] +- [[#Convert selected text elements to sticky notes]] +- [[#Convert text to link with folder and alias]] +- [[#Copy Selected Element Styles to Global]] +- [[#Create new markdown file and embed into active drawing]] +- [[#Darken background color]] +- [[#Elbow connectors]] +- [[#Expand rectangles horizontally keep text centered]] +- [[#Expand rectangles horizontally]] +- [[#Expand rectangles vertically keep text centered]] +- [[#Expand rectangles vertically]] +- [[#Fixed horizontal distance between centers]] +- [[#Fixed inner distance]] +- [[#Fixed spacing]] +- [[#Fixed vertical distance between centers]] +- [[#Fixed vertical distance]] +- [[#Lighten background color]] +- [[Mindmap connector]] +- [[#Modify background color opacity]] +- [[#Normalize Selected Arrows]] +- [[#OCR - Optical Character Recognition]] +- [[#Organic Line]] +- [[#Repeat Elements]] +- [[#Reverse arrows]] +- [[#Scribble Helper]] +- [[#Select Elements of Type]] +- [[#Set background color of unclosed line object by adding a shadow clone]] +- [[#Set Dimensions]] +- [[#Set Font Family]] +- [[#Set Grid]] +- [[#Set Link Alias]] +- [[#Set Stroke Width of Selected Elements]] +- [[#Set Text Alignment]] +- [[#Split text by lines]] +- [[#Toggle Fullscreen on Mobile]] +- [[#Transfer TextElements to Excalidraw markdown metadata]] +- [[#Zoom to Fit Selected Elements]] + +## Add Connector Point +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Connector%20Point.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionThis script will add a small circle to the top left of each text element in the selection and add the text and the "connector point" to a group. You can use the connector points to link text elements with an arrow (in for example a Wardley Map).
+ +## Add Link to Existing File and Open +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Link%20to%20Existing%20File%20and%20Open.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionPrompts for a file from the vault. Adds a link to the selected element pointing to the selected file. You can control in settings to open the file in the current active pane or an adjacent pane.
+ +## Add Link to New Page and Open +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Link%20to%20New%20Page%20and%20Open.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionPrompts for filename. Offers option to create and open a new Markdown or Excalidraw document. Adds link pointing to the new file, to the selected objects in the drawing. You can control in settings to open the file in the current active pane or an adjacent pane.
+ +## Add Next Step in Process +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Next%20Step%20in%20Process.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionThis script will prompt you for the title of the process step, then will create a stick note with the text. If an element is selected then the script will connect this new step with an arrow to the previous step (the selected element). If no element is selected, then the script assumes this is the first step in the process and will only output the sticky note with the text that was entered.
+ +## Box Each Selected Groups +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Box%20Each%20Selected%20Groups.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script will add encapsulating boxes around each of the currently selected groups in Excalidraw.
+ +## Box Selected Elements +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Box%20Selected%20Elements.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionThis script will add an encapsulating box around the currently selected elements in Excalidraw.
+ +## Change shape of selected elements +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Change%20shape%20of%20selected%20elements.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionThe script allows you to change the shape of selected Rectangles, Diamonds and Ellipses.
+ +## Connect elements +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Connect%20elements.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionThis script will connect two objects with an arrow. If either of the objects are a set of grouped elements (e.g. a text element grouped with an encapsulating rectangle), the script will identify these groups, and connect the arrow to the largest object in the group (assuming you want to connect the arrow to the box around the text element).
+ +## Convert freedraw to line +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20freedraw%20to%20line.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionConvert selected freedraw objects into editable lines. This will allow you to adjust your drawings by dragging line points and will also allow you to select shape fill in case of enclosed lines. You can adjust conversion point density in settings.
+ +## Convert selected text elements to sticky notes +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20selected%20text%20elements%20to%20sticky%20notes.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionConverts selected plain text elements to sticky notes with transparent background and transparent stroke color (default setting, can be changed in plugin settings). Essentially converts text element into a wrappable format.
+ +## Convert text to link with folder and alias +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20text%20to%20link%20with%20folder%20and%20alias.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionConverts text elements to links pointing to a file in a selected folder and with the alias set as the original text. The script will prompt the user to select an existing folder from the vault.
original text - [[selected folder/original text|original text]]
+ +## Copy Selected Element Styles to Global +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Copy%20Selected%20Element%20Styles%20to%20Global.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script will copy styles of any selected element into Excalidraw's global styles.
+ +## Create new markdown file and embed into active drawing +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Create%20new%20markdown%20file%20and%20embed%20into%20active%20drawing.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionThe script will prompt you for a filename, then create a new markdown document with the file name provided, open the new markdown document in an adjacent pane, and embed the markdown document into the active Excalidraw drawing.
+ +## Darken background color +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Darken%20background%20color.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script darkens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect. In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.
+ +## Elbow connectors +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Elbow%20connectors.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script converts the selected connectors to elbows.
+ +## Expand rectangles horizontally keep text centered +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20horizontally%20keep%20text%20centered.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script expands the width of the selected rectangles until they are all the same width and keep the text centered.
+ +## Expand rectangles horizontally +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20horizontally.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script expands the width of the selected rectangles until they are all the same width.
+ +## Expand rectangles vertically keep text centered +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20vertically%20keep%20text%20centered.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script expands the height of the selected rectangles until they are all the same height and keep the text centered.
+ +## Expand rectangles vertically +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20vertically.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script expands the height of the selected rectangles until they are all the same height.
+ +## Fixed horizontal distance between centers +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20horizontal%20distance%20between%20centers.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script arranges the selected elements horizontally with a fixed center spacing.
+ +## Fixed inner distance +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20inner%20distance.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script arranges selected elements and groups with a fixed inner distance.
+ +## Fixed spacing +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20spacing.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThe script arranges the selected elements horizontally with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.
+ +## Fixed vertical distance between centers +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20vertical%20distance%20between%20centers.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script arranges the selected elements vertically with a fixed center spacing.
+ +## Fixed vertical distance +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20vertical%20distance.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThe script arranges the selected elements vertically with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.
+ +## Lighten background color +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Lighten%20background%20color.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script lightens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect.In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.
+ +## Mindmap connector +```excalidraw-script-install +https://github.com/xllowl/obsidian-excalidraw-plugin/blob/master/ea-scripts/Mindmap%20connector.md +``` +
Author@xllowl
SourceFile on GitHub
DescriptionThis script creates mindmap like lines(only right side available). The line will starts according to the creation time of the elements. So you may need to create the header element first.
+ +## Modify background color opacity +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Modify%20background%20color%20opacity.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script changes the opacity of the background color of the selected boxes. The default background color in Excalidraw is so dark that the text is hard to read. You can lighten the color a bit by setting transparency. And you can tweak the transparency over and over again until you're happy with it. Although excalidraw has the opacity option in its native property Settings, it also changes the transparency of the border. Use this script to change only the opacity of the background color without affecting the border.
+ +## Normalize Selected Arrows +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Normalize%20Selected%20Arrows.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.
+ +## OCR - Optical Character Recognition +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/OCR%20-%20Optical%20Character%20Recognition.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionREQUIRES EXCALIDRAW 1.5.15
The script will 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and 2) will add the text to your drawing as a text element.
âš  Note that you will need to manually paste your token into the script after the first run! âš 

+ +## Organic Line +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Organic%20Line.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionConverts selected freedraw lines such that pencil pressure will decrease from maximum to minimum from the beginning of the line to its end. The resulting line is placed at the back of the layers, under all other items. Helpful when drawing organic mindmaps.
+ +## Repeat Elements +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Repeat%20Elements.md +``` +
Author@1-2-3
SourceFile on GitHub
DescriptionThis script will detect the difference between 2 selected elements, including position, size, angle, stroke and background color, and create several elements that repeat these differences based on the number of repetitions entered by the user.
+ +## Reverse arrows +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Reverse%20arrows.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionReverse the direction of **arrows** within the scope of selected elements.
+ +## Scribble Helper +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Scribble%20Helper.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptioniOS scribble helper for better handwriting experience with text elements. If no elements are selected then the creates a text element at pointer position and you can use the edit box to modify the text with scribble. If a text element is selected then opens the input prompt where you can modify this text with scribble.
+ +## Select Elements of Type +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Select%20Elements%20of%20Type.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionPrompts you with a list of the different element types in the active image. Only elements of the selected type will be selected on the canvas. If nothing is selected when running the script, then the script will process all the elements on the canvas. If some elements are selected when the script is executed, then the script will only process the selected elements.
The script is useful when, for example, you want to bring to front all the arrows, or want to change the color of all the text elements, etc.
+ +## Set background color of unclosed line object by adding a shadow clone +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionUse this script to set the background color of unclosed (i.e. open) line objects by creating a clone of the object. The script will set the stroke color of the clone to transparent and will add a straight line to close the object. Use settings to define the default background color, the fill style, and the strokeWidth of the clone. By default the clone will be grouped with the original object, you can disable this also in settings.
+ +## Set Dimensions +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Dimensions.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionCurrently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.
+ +## Set Font Family +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Font%20Family.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionSets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.
+ +## Set Grid +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Grid.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionThe default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.
+ +## Set Link Alias +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Link%20Alias.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionIterates all of the links in the selected TextElements and prompts the user to set or modify the alias for each link found.
+ +## Set Stroke Width of Selected Elements +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Stroke%20Width%20of%20Selected%20Elements.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionThis script will set the stroke width of selected elements. This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.
+ +## Set Text Alignment +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Text%20Alignment.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionSets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.
+ +## Split text by lines +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Split%20text%20by%20lines.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionSplit lines of text into separate text elements for easier reorganization
+ +## TheBrain-navigation +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/TheBrain-navigation.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionAn Excalidraw based graph user interface for your Vault. Requires the Breadcrumbs plugin to be installed and configured as well. Generates a user interface similar to that of TheBrain. Watch this introduction to this script on YouTube.
+ +## Toggle Fullscreen on Mobile +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Toggle%20Fullscreen%20on%20Mobile.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionHides Obsidian workspace leaf padding and header (based on option in settings, default is "hide header" = false) which will take Excalidraw to full screen. âš  Note that if the header is not visible, it will be very difficult to invoke the command palette to end full screen. Only hide the header if you have a keyboard or you've practiced opening command palette!
+ +## Transfer TextElements to Excalidraw markdown metadata +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionThe script will delete the selected text elements from the canvas and will copy the text from these text elements into the Excalidraw markdown file as metadata. This means, that the text will no longer be visible in the drawing, however you will be able to search for the text in Obsidian and find the drawing containing this image.
+ +## Zoom to Fit Selected Elements +```excalidraw-script-install +https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.md +``` +
Author@zsviczian
SourceFile on GitHub
DescriptionSimilar to Excalidraw standard SHIFT+2 feature: Zoom to fit selected elements, but with the ability to zoom to 1000%. Inspiration: [#272](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/272)
diff --git a/ea-scripts/directory-info.json b/ea-scripts/directory-info.json index ba6de7e..ca5c862 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":1645305706000},{"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":1645305706000},{"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":1647607808346},{"fname":"Normalize Selected Arrows.svg","mtime":1645960639000},{"fname":"OCR - Optical Character Recognition.md","mtime":1643462434000},{"fname":"OCR - Optical Character Recognition.svg","mtime":1645959546000},{"fname":"Organic Line.md","mtime":1645297209000},{"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":1645305706000},{"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":1642877297000},{"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":1662311422769},{"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":"Transfer TextElements to Excalidraw markdown metadata.md","mtime":1641056885000},{"fname":"Transfer TextElements to Excalidraw markdown metadata.svg","mtime":1645959546000},{"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":"TheBrain-navigation.md","mtime":1650216837464},{"fname":"TheBrain-navigation.svg","mtime":1649614401982},{"fname":"Grid Selected Images.md","mtime":1649614401982},{"fname":"Grid Selected Images.svg","mtime":1649614401982},{"fname":"Palette loader.md","mtime":1662992295615},{"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":1664095143846},{"fname":"Deconstruct selected elements into new drawing.md","mtime":1668541145255},{"fname":"Deconstruct selected elements into new drawing.svg","mtime":1668541145255},{"fname":"Slideshow.md","mtime":1670017348333},{"fname":"Slideshow.svg","mtime":1670017348333},{"fname":"Auto Layout.md","mtime":1670175947081},{"fname":"Auto Layout.svg","mtime":1670175947081},{"fname":"Uniform size.md","mtime":1670175947081},{"fname":"Uniform size.svg","mtime":1670175947081}] \ No newline at end of file +[{"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":1645305706000},{"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":1645305706000},{"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":1647607808346},{"fname":"Normalize Selected Arrows.svg","mtime":1645960639000},{"fname":"Organic Line.md","mtime":1645297209000},{"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":1645305706000},{"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":1642877297000},{"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":1662311422769},{"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":1662992295615},{"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":1664095143846},{"fname":"Deconstruct selected elements into new drawing.md","mtime":1668541145255},{"fname":"Deconstruct selected elements into new drawing.svg","mtime":1668541145255},{"fname":"Slideshow.md","mtime":1670017348333},{"fname":"Slideshow.svg","mtime":1670017348333},{"fname":"Auto Layout.md","mtime":1670175947081},{"fname":"Auto Layout.svg","mtime":1670175947081},{"fname":"Uniform size.md","mtime":1670175947081},{"fname":"Uniform size.svg","mtime":1670175947081}] \ No newline at end of file diff --git a/ea-scripts/index-new.md b/ea-scripts/index-new.md index 75c4e68..72db8da 100644 --- a/ea-scripts/index-new.md +++ b/ea-scripts/index-new.md @@ -58,7 +58,6 @@ I would love to include your contribution in the script library. If you have a s |
|[[#Mindmap connector]]| |
|[[#Modify background color opacity]]| |
|[[#Normalize Selected Arrows]]| -|
|[[#OCR - Optical Character Recognition]]| |
|[[#Organic Line]]| |
|[[#Palette Loader]]| |
|[[#Rename Image]]| @@ -76,7 +75,6 @@ I would love to include your contribution in the script library. If you have a s |
|[[#Slideshow]]| |
|[[#Split text by lines]]| |
|[[#Text Arch]]| -|
|[[#Transfer TextElements to Excalidraw markdown metadata]]| |
|[[#Uniform Size]]| |
|[[#Zoom to Fit Selected Elements]]| @@ -266,12 +264,6 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea ```
Author@1-2-3
SourceFile on GitHub
DescriptionThis script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.
-## OCR - Optical Character Recognition -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/OCR%20-%20Optical%20Character%20Recognition.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionREQUIRES EXCALIDRAW 1.5.15
The script will 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and 2) will add the text to your drawing as a text element.
âš  Note that you will need to manually paste your token into the script after the first run! âš 

- ## Organic Line ```excalidraw-script-install https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Organic%20Line.md @@ -374,12 +366,6 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea ```
Author@zsviczian
SourceFile on GitHub
DescriptionFit a text to the arch of a circle. The script will prompt you for the radius of the circle and then split your text to individual letters and place each letter to the arch defined by the radius. Setting a lower radius value will increase the arching of the text. Note that the arched-text will no longer be editable as a text element and it will no longer function as a markdown link. Emojis are currently not supported.
-## Transfer TextElements to Excalidraw markdown metadata -```excalidraw-script-install -https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md -``` -
Author@zsviczian
SourceFile on GitHub
DescriptionThe script will delete the selected text elements from the canvas and will copy the text from these text elements into the Excalidraw markdown file as metadata. This means, that the text will no longer be visible in the drawing, however you will be able to search for the text in Obsidian and find the drawing containing this image.
- ## Uniform Size ```excalidraw-script-install https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Uniform%20size.md