diff --git a/docs/ExcalidrawScriptsEngine.md b/docs/ExcalidrawScriptsEngine.md
index f24a679..f56ae3d 100644
--- a/docs/ExcalidrawScriptsEngine.md
+++ b/docs/ExcalidrawScriptsEngine.md
@@ -26,9 +26,52 @@ An Excalidraw script will automatically receive two objects:
- `inputPrompt: (header: string, placeholder?: string, value?: string)`
- Opens a prompt that asks for an input. Returns a string with the input.
- You need to await the result of inputPrompt.
- - `suggester: (displayItems: string[], actualItems: string[])`
- - Opens a suggester. Displays the displayItems, but you map these the other values with actualItems. Returns the selected value.
+ - `suggester: (displayItems: string[], items: any[], hint?: string, instructions?:Instruction[])`
+ - Opens a suggester. Displays the displayItems and returns the corresponding item from items[].
- You need to await the result of suggester.
+ - If the user cancels (ESC), suggester will return `undefined`
+ - Hint and instructions are optional.
+ ```typescript
+ interface Instruction {
+ command: string;
+ purpose: string;
+ }
+ ```
+ - Scripts may have settings. These settings are stored as part of plugin settings and may be also changed by the user via the Obsidian plugin settings window.
+ - You can access settings for the active script using `ea.getScriptSettings()` and store settings values with `ea.setScriptSettings(settings:any)`
+ - Rules for displaying script settings in plugin settings are:
+ - If the setting is a simple literal (boolean, number, string) these will be displayed as such in settings. The name of the setting will be the key for the value.
+ ```javascript
+ ea.setScriptSettings({
+ "value 1": true,
+ "value 2": 1,
+ "value 3": "my string"
+ })
+ ```
+ 
+ - If the setting is an object and follows the below structure then a description and a valueset may also be added. Values may also be hidden from the user using the `hidden` key.
+ ```javascript
+ ea.setScriptSettings({
+ "value 1": {
+ "value": true,
+ "description": "This is the description for my boolean value"
+ },
+ "value 2": {
+ "value": 1,
+ "description": "This is the description for my numeric value"
+ },
+ "value 3": {
+ "value": "my string",
+ "description": "This is the description for my string value",
+ "valueset": ["allowed 1","allowed 2","allowed 3"]
+ },
+ "value 4": {
+ "value": "my value",
+ "hidden": true
+ }
+ });
+ ```
+ 
---------
@@ -42,16 +85,41 @@ These scripts are available as downloadable `.md` files on GitHub in [this](http
This script will add an encapsulating box around the currently selected elements in Excalidraw
```javascript
-//uncomment if you want a prompt for custom padding
-//const padding = parseInt (await utils.inputPrompt("padding?","number","10"));
-const padding = 10
+if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
+ new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
+ return;
+}
+
+settings = ea.getScriptSettings();
+//check if settings exist. If not, set default values on first run
+if(!settings["Default padding"]) {
+ settings = {
+ "Prompt for padding?": true,
+ "Default padding" : {
+ value: 10,
+ description: "Padding between the bounding box of the selected elements, and the box the script creates"
+ }
+ };
+ ea.setScriptSettings(settings);
+}
+
+let padding = settings["Default padding"].value;
+
+if(settings["Prompt for padding?"]) {
+ padding = parseInt (await utils.inputPrompt("padding?","number",padding.toString()));
+}
+
+if(isNaN(padding)) {
+ new Notice("The padding value provided is not a number");
+ return;
+}
elements = ea.getViewSelectedElements();
const box = ea.getBoundingBox(elements);
color = ea
.getExcalidrawAPI()
.getAppState()
.currentItemStrokeColor;
-//uncomment if you want to set the stroke to a random color
+//uncomment for random color:
//color = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,"0");
ea.style.strokeColor = color;
id = ea.addRect(
@@ -73,20 +141,76 @@ ea.addElementsToView(false);
This 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).
```javascript
+if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
+ new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
+ return;
+}
+
+settings = ea.getScriptSettings();
+//set default values on first run
+if(!settings["Starting arrowhead"]) {
+ settings = {
+ "Starting arrowhead" : {
+ value: "none",
+ valueset: ["none","arrow","triangle","bar","dot"]
+ },
+ "Ending arrowhead" : {
+ value: "triangle",
+ valueset: ["none","arrow","triangle","bar","dot"]
+ },
+ "Line points" : {
+ value: 1,
+ description: "Number of line points between start and end"
+ }
+ };
+ ea.setScriptSettings(settings);
+}
+
+const arrowStart = settings["Starting arrowhead"].value === "none" ? null : settings["Starting arrowhead"].value;
+const arrowEnd = settings["Ending arrowhead"].value === "none" ? null : settings["Ending arrowhead"].value;
+const linePoints = Math.floor(settings["Line points"].value);
+
const elements = ea.getViewSelectedElements();
ea.copyViewElementsToEAforEditing(elements);
-const groups = ea.getMaximumGroups(elements);
-if(groups.length !== 2) return;
+groups = ea.getMaximumGroups(elements);
+
+if(groups.length !== 2) {
+ //unfortunately getMaxGroups returns duplicated resultset for sticky notes
+ //needs additional filtering
+ cleanGroups=[];
+ idList = [];
+ for (group of groups) {
+ keep = true;
+ for(item of group) if(idList.contains(item.id)) keep = false;
+ if(keep) {
+ cleanGroups.push(group);
+ idList = idList.concat(group.map(el=>el.id))
+ }
+ }
+ if(cleanGroups.length !== 2) return;
+ groups = cleanGroups;
+}
+
els = [
ea.getLargestElement(groups[0]),
ea.getLargestElement(groups[1])
];
+
+ea.style.strokeColor = els[0].strokeColor;
+ea.style.strokeWidth = els[0].strokeWidth;
+ea.style.strokeStyle = els[0].strokeStyle;
+ea.style.strokeSharpness = els[0].strokeSharpness;
+
ea.connectObjects(
els[0].id,
null,
els[1].id,
null,
- {numberOfPoints:2}
+ {
+ endArrowHead: arrowEnd,
+ startArrowHead: arrowStart,
+ numberOfPoints: linePoints
+ }
);
ea.addElementsToView();
```
diff --git a/ea-scripts/Add Link and Open Page.md b/ea-scripts/Add Link and Open Page.md
new file mode 100644
index 0000000..8311f03
--- /dev/null
+++ b/ea-scripts/Add Link and Open Page.md
@@ -0,0 +1,72 @@
+/*
+
+
+Prompt for a file in the vault. Add a link above or below (based on settings) the selected element, to the selected file. If no file is selected then the script creates a new file following the default filename defined for excalidraw embeds.
+
+```javascript
+*/
+
+if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
+ new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
+ return;
+}
+
+settings = ea.getScriptSettings();
+//set default values on first run
+if(!settings["Link position"]) {
+ settings = {
+ "Link position" : {
+ value: "below",
+ valueset: ["above","below"],
+ description: "Add link below or above the selected object?"
+ },
+ "Link font size" : {
+ value: 12
+ }
+ };
+ ea.setScriptSettings(settings);
+}
+
+const below = settings["Link position"].value === "below";
+const fontSize = Math.floor(settings["Link font size"].value);
+
+elements = ea.getViewSelectedElements();
+if(elements.length === 0) {
+ new Notice("No selected elements");
+ return;
+}
+
+const files = app.vault.getFiles()
+const filePaths = files.map((f)=>f.path);
+file = await utils.suggester(filePaths,files,"Select file or press ESC to create a new document");
+
+alias = null;
+if(file) {
+ alias = file.basename;
+} else {
+ const prefix = ea.targetView.file.path.substring(0,ea.targetView.file.path.length-3);
+ const timestamp = moment(Date.now()).format(ea.plugin.settings.drawingFilenameDateTime);
+ file = await app.vault.create(`${prefix} ${timestamp}.md`,"");
+}
+
+const filepath = app.metadataCache.fileToLinktext(file,ea.targetView.file.path,true);
+
+ea.style.textAlign = "center";
+ea.style.fontSize = fontSize;
+const textElementsIfAny = elements.filter(el=>el.type==="text");
+if(textElementsIfAny.length>0) ea.style.fontFamily = textElementsIfAny[0].fontFamily;
+ea.style.strokeColor = elements[0].strokeColor;
+
+
+const box = ea.getBoundingBox(elements);
+const linkText = `[[${filepath}${alias?"|"+alias:""}]]`;
+const size = ea.measureText(alias?ea.plugin.settings.linkPrefix+alias:linkText);
+const id = ea.addText(
+ box.topX+(box.width-size.width)/2,
+ below ? box.topY + box.height + size.height : box.topY - size.height - 3,
+ linkText
+);
+ea.copyViewElementsToEAforEditing(elements);
+ea.addToGroup(elements.map((e)=>e.id).concat([id]));
+ea.addElementsToView(false);
+ea.openFileInNewOrAdjacentLeaf(file);
\ No newline at end of file
diff --git a/ea-scripts/Add Next Step in Process.md b/ea-scripts/Add Next Step in Process.md
index 2ada614..edb18a7 100644
--- a/ea-scripts/Add Next Step in Process.md
+++ b/ea-scripts/Add Next Step in Process.md
@@ -5,13 +5,57 @@ This script will prompt you for the title of the process step, then will create
```javascript
*/
+
+if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
+ new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
+ return;
+}
+
+settings = ea.getScriptSettings();
+//set default values on first run
+if(!settings["Starting arrowhead"]) {
+ settings = {
+ "Starting arrowhead" : {
+ value: "none",
+ valueset: ["none","arrow","triangle","bar","dot"]
+ },
+ "Ending arrowhead" : {
+ value: "triangle",
+ valueset: ["none","arrow","triangle","bar","dot"]
+ },
+ "Line points" : {
+ value: 0,
+ description: "Number of line points between start and end"
+ },
+ "Gap between elements": {
+ value: 100
+ },
+ "Wrap text at (number of characters)": {
+ value: 25,
+ },
+ "Fix width": {
+ value: true,
+ description: "The object around the text should have fix width to fit the wrapped text"
+ }
+ };
+ ea.setScriptSettings(settings);
+}
+
+const arrowStart = settings["Starting arrowhead"].value === "none" ? null : settings["Starting arrowhead"].value;
+const arrowEnd = settings["Ending arrowhead"].value === "none" ? null : settings["Ending arrowhead"].value;
+const linePoints = Math.floor(settings["Line points"].value);
+const gapBetweenElements = Math.floor(settings["Gap between elements"].value);
+const wrapLineLen = Math.floor(settings["Wrap text at (number of characters)"].value);
+const fixWidth = settings["Fix width"];
+
const textPadding = 10;
-const gapBetweenElements = 50;
-const wrapLineLen = 25;
const text = await utils.inputPrompt("Text?");
const elements = ea.getViewSelectedElements();
const isFirst = (!elements || elements.length === 0);
+const width = ea.measureText("w".repeat(wrapLineLen)).width;
+console.log(width,fixWidth);
+
if(!isFirst) {
const fromElement = ea.getLargestElement(elements);
ea.copyViewElementsToEAforEditing([fromElement]);
@@ -27,6 +71,7 @@ if(!isFirst) {
ea.style.strokeSharpness = el.strokeSharpness;
}
+
const id = ea.addText(
fromElement.x,
fromElement.y+fromElement.height+gapBetweenElements,
@@ -35,7 +80,8 @@ if(!isFirst) {
wrapAt: wrapLineLen,
textAlign: "center",
box: "rectangle",
- boxPadding: textPadding
+ boxPadding: textPadding,
+ ...fixWidth?{width: width}:null
}
);
@@ -45,9 +91,9 @@ if(!isFirst) {
id,
null,
{
- endArrowHead: "triangle",
- startArrowHead: null,
- numberOfPoints: 0
+ endArrowHead: arrowEnd,
+ startArrowHead: arrowStart,
+ numberOfPoints: linePoints
}
);
ea.addElementsToView(false);
@@ -60,7 +106,8 @@ if(!isFirst) {
wrapAt: wrapLineLen,
textAlign: "center",
box: "rectangle",
- boxPadding: textPadding
+ boxPadding: textPadding,
+ ...fixWidth?{width: width}:null
}
);
ea.addElementsToView(true);
diff --git a/ea-scripts/Box Selected Elements.md b/ea-scripts/Box Selected Elements.md
index fe43b58..165503d 100644
--- a/ea-scripts/Box Selected Elements.md
+++ b/ea-scripts/Box Selected Elements.md
@@ -1,9 +1,4 @@
/*
-
-
-
-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.
-

This script will add an encapsulating box around the currently selected elements in Excalidraw.
@@ -13,9 +8,29 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
```javascript
*/
-//uncomment if you don't want a prompt for custom padding
-//const padding = 10
-const padding = parseInt (await utils.inputPrompt("padding?","number","10"));
+if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
+ new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
+ return;
+}
+settings = ea.getScriptSettings();
+//set default values on first run
+if(!settings["Default padding"]) {
+ settings = {
+ "Prompt for padding?": true,
+ "Default padding" : {
+ value: 10,
+ description: "Padding between the bounding box of the selected elements, and the box the script creates"
+ }
+ };
+ ea.setScriptSettings(settings);
+}
+
+let padding = settings["Default padding"].value;
+
+if(settings["Prompt for padding?"]) {
+ padding = parseInt (await utils.inputPrompt("padding?","number",padding.toString()));
+}
+
if(isNaN(padding)) {
new Notice("The padding value provided is not a number");
return;
diff --git a/ea-scripts/Change shape of selected elements.md b/ea-scripts/Change shape of selected elements.md
new file mode 100644
index 0000000..ebf371c
--- /dev/null
+++ b/ea-scripts/Change shape of selected elements.md
@@ -0,0 +1,15 @@
+/*
+
+
+The script allows you to change the shape of selected Rectangles, Diamonds and Ellipses.
+
+```javascript
+*/
+const shapes=["ellipse","rectangle","diamond"];
+elements = ea.getViewSelectedElements().filter(el=>shapes.contains(el.type));
+newShape = await utils.suggester(shapes, shapes);
+if(!newShape) return;
+
+elements.forEach(el=>el.type = newShape);
+ea.copyViewElementsToEAforEditing(elements);
+ea.addElementsToView();
\ No newline at end of file
diff --git a/ea-scripts/Connect elements.md b/ea-scripts/Connect elements.md
index 9623137..a83da71 100644
--- a/ea-scripts/Connect elements.md
+++ b/ea-scripts/Connect elements.md
@@ -8,6 +8,37 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
```javascript
*/
+if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
+ new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
+ return;
+}
+
+settings = ea.getScriptSettings();
+//set default values on first run
+if(!settings["Starting arrowhead"]) {
+ settings = {
+ "Starting arrowhead" : {
+ value: "none",
+ valueset: ["none","arrow","triangle","bar","dot"]
+ },
+ "Ending arrowhead" : {
+ value: "triangle",
+ valueset: ["none","arrow","triangle","bar","dot"]
+ },
+ "Line points" : {
+ value: 1,
+ description: "Number of line points between start and end"
+ }
+ };
+ ea.setScriptSettings(settings);
+}
+
+const arrowStart = settings["Starting arrowhead"].value === "none" ? null : settings["Starting arrowhead"].value;
+const arrowEnd = settings["Ending arrowhead"].value === "none" ? null : settings["Ending arrowhead"].value;
+const linePoints = Math.floor(settings["Line points"].value);
+
+
+
const elements = ea.getViewSelectedElements();
ea.copyViewElementsToEAforEditing(elements);
groups = ea.getMaximumGroups(elements);
@@ -31,6 +62,7 @@ els = [
ea.getLargestElement(groups[0]),
ea.getLargestElement(groups[1])
];
+
ea.style.strokeColor = els[0].strokeColor;
ea.style.strokeWidth = els[0].strokeWidth;
ea.style.strokeStyle = els[0].strokeStyle;
@@ -41,9 +73,9 @@ ea.connectObjects(
els[1].id,
null,
{
- endArrowHead: "triangle",
- startArrowHead: "", //"dot",
- numberOfPoints: 1
+ endArrowHead: arrowEnd,
+ startArrowHead: arrowStart,
+ numberOfPoints: linePoints
}
);
ea.addElementsToView();
\ No newline at end of file
diff --git a/ea-scripts/Convert selected text elements to sticky notes.md b/ea-scripts/Convert selected text elements to sticky notes.md
new file mode 100644
index 0000000..954d092
--- /dev/null
+++ b/ea-scripts/Convert selected text elements to sticky notes.md
@@ -0,0 +1,51 @@
+/*
+
+
+Converts selected plain text elements to sticky notes with transparent background and transparent stroke color. Essentially converts text element into a wrappable format.
+
+```javascript
+*/
+if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
+ new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
+ return;
+}
+
+settings = ea.getScriptSettings();
+//set default values on first run
+if(!settings["Border color"]) {
+ settings = {
+ "Border color" : {
+ value: "#000000",
+ description: "Any legal HTML color (#000000, rgb, color-name, etc.). Set to 'transparent' for transparent color."
+ },
+ "Background color" : {
+ value: "transparent",
+ description: "Background color of the sticky note. Set to 'transparent' for transparent color."
+ },
+ "Background fill style" : {
+ value: "solid",
+ description: "Fill style of the sticky note",
+ valueset: ["hachure","cross-hatch","solid"]
+ }
+ };
+ ea.setScriptSettings(settings);
+}
+
+const strokeColor = settings["Border color"].value;
+const backgroundColor = settings["Background color"].value;
+const fillStyle = settings["Background fill style"].value;
+
+elements = ea.getViewSelectedElements()
+ .filter((el)=>(el.type==="text")&&(el.containerId===null));
+if(elements.length===0) return;
+ea.style.strokeColor = strokeColor;
+ea.style.backgroundColor = backgroundColor;
+ea.style.fillStyle = fillStyle;
+const padding = 6;
+elements.forEach((el)=>{
+ const id = ea.addRect(el.x-padding,el.y-padding,el.width+2*padding,el.height+2*padding);
+ ea.getElement(id).boundElements=[{type:"text",id:el.id}];
+ el.containerId = id;
+});
+ea.copyViewElementsToEAforEditing(elements);
+ea.addElementsToView();
\ No newline at end of file
diff --git a/ea-scripts/Convert selected text elements to transparent sticky notes.md b/ea-scripts/Convert selected text elements to transparent sticky notes.md
deleted file mode 100644
index afcec93..0000000
--- a/ea-scripts/Convert selected text elements to transparent sticky notes.md
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-
-
-Converts selected plain text elements to sticky notes with transparent background and transparent stroke color. Essentially converts text element into a wrappable format.
-
-```javascript
-*/
-elements = ea.getViewSelectedElements()
- .filter((el)=>(el.type==="text")&&(el.containerId===null));
-if(elements.length===0) return;
-ea.style.strokeColor = "transparent";
-ea.style.backgroundColor = "transparent"
-const padding = 6;
-elements.forEach((el)=>{
- const id = ea.addRect(el.x-padding,el.y-padding,el.width+2*padding,el.height+2*padding);
- ea.getElement(id).boundElements=[{type:"text",id:el.id}];
- el.containerId = id;
-});
-ea.copyViewElementsToEAforEditing(elements);
-ea.addElementsToView();
\ No newline at end of file
diff --git a/ea-scripts/OCR - Optical Character Recognition.md b/ea-scripts/OCR - Optical Character Recognition.md
index 44dd186..096d00b 100644
--- a/ea-scripts/OCR - Optical Character Recognition.md
+++ b/ea-scripts/OCR - Optical Character Recognition.md
@@ -1,8 +1,4 @@
/*
-
-
-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.
-

THIS SCRIPT REQUIRES EXCALIDRAW 1.5.15
@@ -20,12 +16,19 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
```javascript
*/
-const curVersion = app.plugins.manifests["obsidian-excalidraw-plugin"].version;
-if(curVersion < "1.5.15") new Notice("please update Excalidraw plugin to the latest version");
+if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
+ new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
+ return;
+}
-let token = ea.getScriptSettings()?.token;
+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(
@@ -35,7 +38,7 @@ if (!token) {
if (tokenResponse.status === 200) {
jsonResponse = await tokenResponse.json();
token = jsonResponse.token;
- ea.setScriptSettings({token});
+ ea.setScriptSettings({token: {value: token, hidden: true}});
} else {
notice(`Taskbone OCR Error: ${tokenResponse.status}\nPlease try again later.`);
return;
diff --git a/ea-scripts/README.md b/ea-scripts/README.md
index c55c94a..5f51f2a 100644
--- a/ea-scripts/README.md
+++ b/ea-scripts/README.md
@@ -15,10 +15,13 @@ Open the script you are interested in and save it to your Obsidian Vault includi
|Title|Description|Icon|Contributor|
|----|----|----|----|
|[Add Connector Point](Add%20Connector%20Point.md)|This script will add a small circle to the top left of each text element in the selection and add the text and the "bullet point" into a group.||[@zsviczian](https://github.com/zsviczian)|
+|[Add Link and Open Page](Add%20Link%20and%20Open%20Page.md)|Prompt for a file in the vault. Add a link above or below (based on settings) the selected element, to the selected file. If no file is selected then the script creates a new file following the default filename defined for excalidraw embeds.||[@zsviczian](https://github.com/zsviczian)|
+|[Add Next Step in Process](Add%20Next%20Step%20in%20Process.md)|This 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.|![]https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-process-step.jpg|[@zsviczian](https://github.com/zsviczian)|
|[Box Each Selected Groups](Box%20Each%20Selected%20Groups.md)|This script will add encapsulating boxes around each of the currently selected groups in Excalidraw.||[@1-2-3](https://github.com/1-2-3)|
|[Box Selected Elements](Box%20Selected%20Elements.md)|This script will add an encapsulating box around the currently selected elements in Excalidraw.||[@zsviczian](https://github.com/zsviczian)|
|[Connect elements](Connect%20elements.md)|This 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).||[@zsviczian](https://github.com/zsviczian)|
-|[Convert selected text elements to transparent sticky notes](Convert%20selected%20text%20elements%20to%20transparent%20sticky%20notes.md)|Converts selected plain text elements to sticky notes with transparent background and transparent stroke color. Essentially converts text element into a wrappable format.||[@zsviczian](https://github.com/zsviczian)|
+|[Change shape of selected elements](Change%20shape%20of%20selected%20elements.md)|The script allows you to change the shape of selected Rectangles, Diamonds and Ellipses||[@zsviczian](https://github.com/zsviczian)|
+|[Convert selected text elements to sticky notes](Convert%20selected%20text%20elements%20to%20sticky%20notes.md)|Converts selected plain text elements to sticky notes with transparent background and transparent stroke color. Essentially converts text element into a wrappable format.||[@zsviczian](https://github.com/zsviczian)|
|[Convert text to link with folder and alias](Convert%20text%20to%20link%20with%20folder%20and%20alias.md)|Converts 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]]`|[@zsviczian](https://github.com/zsviczian)|
|[Copy Selected Element Styles to Global](Copy%20Selected%20Element%20Styles%20to%20Global)|This script will copy styles of any selected element into Excalidraw's global styles.||[@1-2-3](https://github.com/1-2-3)|
|[Create new markdown file and embed into active drawing](Create%20new%20markdown%20file%20and%20embed%20into%20active%20drawing.md)|The 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.||[@zsviczian](https://github.com/zsviczian)|
diff --git a/ea-scripts/index.md b/ea-scripts/index.md
index c6dcf1b..262eef9 100644
--- a/ea-scripts/index.md
+++ b/ea-scripts/index.md
@@ -26,11 +26,13 @@ I would love to include your contribution in the script library. If you have a s
# List of available scripts
- [[#Add Connector Point]]
+- [[#Add Link and Open Page]]
- [[#Add Next Step in Process]]
- [[#Box Each Selected Groups]]
- [[#Box Selected Elements]]
+- [[#Change shape of selected elements]]
- [[#Connect elements]]
-- [[#Convert selected text elements to transparent sticky notes]]
+- [[#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]]
@@ -63,13 +65,18 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
| Author | @zsviczian |
| Source | File on GitHub |
| Description | This 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 and Open Page
+```excalidraw-script-install
+https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Link%20and%20Open%20Page.md
+```
+| Author | @zsviczian |
| Source | File on GitHub |
| Description | Prompt for a file in the vault. Add a link above or below (based on settings) the selected element, to the selected file. If no file is selected then the script creates a new file following the default filename defined for excalidraw embeds.
 |
+
## 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 |
| Source | File on GitHub |
| Description | This 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
@@ -82,17 +89,23 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
| Author | @zsviczian |
| Source | File on GitHub |
| Description | This 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 |
| Source | File on GitHub |
| Description | The 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 |
| Source | File on GitHub |
| Description | This 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 selected text elements to transparent sticky notes
+## 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%20transparent%20sticky%20notes.md
+https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20selected%20text%20elements%20to%20sticky%20notes.md
```
-| Author | @zsviczian |
| Source | File on GitHub |
| Description | Converts selected plain text elements to sticky notes with transparent background and transparent stroke color. Essentially converts text element into a wrappable format.
 |
+| Author | @zsviczian |
| Source | File on GitHub |
| Description | Converts 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
diff --git a/manifest.json b/manifest.json
index 2b13f74..3a7689f 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
- "version": "1.5.20",
+ "version": "1.5.21",
"minAppVersion": "0.12.16",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",
diff --git a/src/EmbeddedFileLoader.ts b/src/EmbeddedFileLoader.ts
index fb3ae7c..527b15e 100644
--- a/src/EmbeddedFileLoader.ts
+++ b/src/EmbeddedFileLoader.ts
@@ -399,7 +399,7 @@ const convertMarkdownToSVG = async (
fontDef = "";
break;
default:
- const font = await getFontDataURL(plugin.app,fontName,file.path);
+ const font = await getFontDataURL(plugin.app, fontName, file.path);
fontDef = font.fontDef;
fontName = font.fontName;
}
diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts
index 03a4306..2ce349a 100644
--- a/src/ExcalidrawAutomate.ts
+++ b/src/ExcalidrawAutomate.ts
@@ -6,7 +6,7 @@ import {
ExcalidrawElement,
ExcalidrawBindableElement,
} from "@zsviczian/excalidraw/types/element/types";
-import { Component, MarkdownRenderer, normalizePath, TFile, WorkspaceLeaf } from "obsidian";
+import { normalizePath, TFile, WorkspaceLeaf } from "obsidian";
import ExcalidrawView, { ExportSettings, TextMode } from "./ExcalidrawView";
import { ExcalidrawData } from "./ExcalidrawData";
import {
@@ -171,7 +171,7 @@ export interface ExcalidrawAutomate {
},
): boolean;
addElementsToView( //Adds elements from elementsDict to the current view
- repositionToCursor?: boolean, //default is false
+ repositionToCursor?: boolean, //default is false
save?: boolean, //default is true
//newElementsOnTop controls whether elements created with ExcalidrawAutomate
//are added at the bottom of the stack or the top of the stack of elements already in the view
@@ -222,13 +222,13 @@ export interface ExcalidrawAutomate {
//See OCR plugin for example on how to use scriptSettings
activeScript: string; //Set automatically by the ScriptEngine
getScriptSettings(): {}; //Returns script settings. Saves settings in plugin settings, under the activeScript key
- setScriptSettings(settings:any):Promise; //sets script settings.
- openFileInNewOrAdjacentLeaf (file:TFile):WorkspaceLeaf;//Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings
- measureText(text:string):{ width: number, height: number }; //measure text size based on current style settings
+ setScriptSettings(settings: any): Promise; //sets script settings.
+ openFileInNewOrAdjacentLeaf(file: TFile): WorkspaceLeaf; //Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings
+ measureText(text: string): { width: number; height: number }; //measure text size based on current style settings
//verifyMinimumPluginVersion returns true if plugin version is >= than required
- //recommended use:
+ //recommended use:
//if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.20")) {new Notice("message");return;}
- verifyMinimumPluginVersion(requiredVersion: string):boolean;
+ verifyMinimumPluginVersion(requiredVersion: string): boolean;
}
declare let window: any;
@@ -237,7 +237,7 @@ export async function initExcalidrawAutomate(
plugin: ExcalidrawPlugin,
): Promise {
window.ExcalidrawAutomate = {
- plugin: plugin,
+ plugin,
elementsDict: {},
imagesDict: {},
style: {
@@ -306,8 +306,8 @@ export async function initExcalidrawAutomate(
this.style.strokeSharpness = 3;
return getFontFamily(3);
default:
- this.style.strokeSharpness = 1;
- return getFontFamily(1);
+ this.style.strokeSharpness = 1;
+ return getFontFamily(1);
}
},
setTheme(val: number) {
@@ -1192,19 +1192,19 @@ export async function initExcalidrawAutomate(
async addElementsToView(
repositionToCursor: boolean = false,
save: boolean = true,
- newElementsOnTop: boolean = false
+ newElementsOnTop: boolean = false,
): Promise {
if (!this.targetView || !this.targetView?._loaded) {
errorMessage("targetView not set", "addElementsToView()");
return false;
}
const elements = this.getElements();
- return await this.targetView.addElements(
+ return await this.targetView.addElements(
elements,
repositionToCursor,
save,
this.imagesDict,
- newElementsOnTop
+ newElementsOnTop,
);
},
onDropHook: null,
@@ -1263,30 +1263,41 @@ export async function initExcalidrawAutomate(
},
activeScript: null,
getScriptSettings(): {} {
- if(!this.activeScript) return null;
- return this.plugin.settings.scriptEngineSettings[this.activeScript];
+ if (!this.activeScript) {
+ return null;
+ }
+ return this.plugin.settings.scriptEngineSettings[this.activeScript] ?? {};
},
- async setScriptSettings(settings:any): Promise {
- if(!this.activeScript) return null;
+ async setScriptSettings(settings: any): Promise {
+ if (!this.activeScript) {
+ return null;
+ }
this.plugin.settings.scriptEngineSettings[this.activeScript] = settings;
await this.plugin.saveSettings();
},
- openFileInNewOrAdjacentLeaf (file:TFile):WorkspaceLeaf {
- if(!file || !(file instanceof TFile)) return null;
- if(!this.targetView) return null;
- const leaf = getNewOrAdjacentLeaf(this.plugin,this.targetView.leaf);
+ openFileInNewOrAdjacentLeaf(file: TFile): WorkspaceLeaf {
+ if (!file || !(file instanceof TFile)) {
+ return null;
+ }
+ if (!this.targetView) {
+ return null;
+ }
+ const leaf = getNewOrAdjacentLeaf(this.plugin, this.targetView.leaf);
leaf.openFile(file);
return leaf;
},
- measureText(text:string):{ width: number, height: number } {
- const size = measureText(text,this.style.fontSize,this.style.fontFamily);
- return {width: size.w, height: size.h};
+ measureText(text: string): { width: number; height: number } {
+ const size = measureText(
+ text,
+ this.style.fontSize,
+ this.style.fontFamily,
+ );
+ return { width: size.w, height: size.h };
},
- verifyMinimumPluginVersion(requiredVersion: string):boolean {
+ verifyMinimumPluginVersion(requiredVersion: string): boolean {
const manifest = this.plugin.app.plugins.manifests[PLUGIN_ID];
return manifest.version >= requiredVersion;
-
- }
+ },
};
await initFonts();
return window.ExcalidrawAutomate;
@@ -1575,7 +1586,7 @@ export async function createSVG(
if (template?.hasSVGwithBitmap) {
svg.setAttribute("hasbitmap", "true");
}
- return embedFont ? embedFontsInSVG(svg,plugin) : svg;
+ return embedFont ? embedFontsInSVG(svg, plugin) : svg;
}
function estimateLineBound(points: any): [number, number, number, number] {
diff --git a/src/ExcalidrawData.ts b/src/ExcalidrawData.ts
index 663a725..cfa6e5d 100644
--- a/src/ExcalidrawData.ts
+++ b/src/ExcalidrawData.ts
@@ -383,9 +383,9 @@ export class ExcalidrawData {
forceUpdate: boolean = false,
) {
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/376
- if (sceneTextElement.containerId) {
+ if (sceneTextElement.containerId) {
return; //I leave the setting of text size to excalidraw, when text is in a container
- //because text width is fixed to the container width
+ //because text width is fixed to the container width
}
if (forceUpdate || newText != sceneTextElement.text) {
const measure = measureText(
diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts
index 0b36073..67ee85a 100644
--- a/src/ExcalidrawView.ts
+++ b/src/ExcalidrawView.ts
@@ -205,7 +205,9 @@ export default class ExcalidrawView extends TextFileView {
return;
}
const serializer = new XMLSerializer();
- const svgString = serializer.serializeToString(embedFontsInSVG(svg,this.plugin));
+ const svgString = serializer.serializeToString(
+ embedFontsInSVG(svg, this.plugin),
+ );
if (file && file instanceof TFile) {
await this.app.vault.modify(file, svgString);
} else {
@@ -824,7 +826,7 @@ export default class ExcalidrawView extends TextFileView {
: this.excalidrawAPI.getAppState().zenModeEnabled;
//debug({where:"ExcalidrawView.loadDrawing",file:this.file.name,dataTheme:excalidrawData.appState.theme,before:"updateScene"})
this.excalidrawAPI.setLocalFont(
- this.plugin.settings.experimentalEnableFourthFont
+ this.plugin.settings.experimentalEnableFourthFont,
);
this.excalidrawAPI.updateScene({
@@ -1011,7 +1013,7 @@ export default class ExcalidrawView extends TextFileView {
if (!svg) {
return null;
}
- svg = embedFontsInSVG(svg,this.plugin);
+ svg = embedFontsInSVG(svg, this.plugin);
download(
null,
svgToBase64(svg.outerHTML),
@@ -1075,9 +1077,9 @@ export default class ExcalidrawView extends TextFileView {
//console.log({where:"ExcalidrawView.React.ReadyPromise"});
//debug({where:"ExcalidrawView.React.useEffect",file:this.file.name,before:"this.loadSceneFiles"});
this.excalidrawAPI.setLocalFont(
- this.plugin.settings.experimentalEnableFourthFont
+ this.plugin.settings.experimentalEnableFourthFont,
);
-
+
this.loadSceneFiles();
this.updateContainerSize(null, true);
});
@@ -1291,11 +1293,10 @@ export default class ExcalidrawView extends TextFileView {
}
const st: AppState = this.excalidrawAPI.getAppState();
-
- const elements =
- newElementsOnTop
+
+ const elements = newElementsOnTop
? el.concat(newElements.filter((e) => !removeList.includes(e.id)))
- : (newElements.filter((e) => !removeList.includes(e.id))).concat(el);
+ : newElements.filter((e) => !removeList.includes(e.id)).concat(el);
this.excalidrawAPI.updateScene({
elements,
appState: st,
@@ -1550,23 +1551,20 @@ export default class ExcalidrawView extends TextFileView {
let linktext = "";
const selectedElement = getTextElementAtPointer(currentPosition);
if (!selectedElement || !selectedElement.text) {
- const selectedImgElement =
- getImageElementAtPointer(currentPosition);
+ const selectedImgElement = getImageElementAtPointer(currentPosition);
if (!selectedImgElement || !selectedImgElement.fileId) {
return;
}
if (!this.excalidrawData.hasFile(selectedImgElement.fileId)) {
return;
}
- const ef = this.excalidrawData.getFile(
- selectedImgElement.fileId,
- );
+ const ef = this.excalidrawData.getFile(selectedImgElement.fileId);
const ref = ef.linkParts.ref
? `#${ef.linkParts.isBlockRef ? "^" : ""}${ef.linkParts.ref}`
: "";
linktext =
- this.excalidrawData.getFile(selectedImgElement.fileId).file
- .path + ref;
+ this.excalidrawData.getFile(selectedImgElement.fileId).file.path +
+ ref;
} else {
const text: string =
this.textMode === TextMode.parsed
@@ -1611,7 +1609,7 @@ export default class ExcalidrawView extends TextFileView {
}
}, 100);
}
- }
+ };
const excalidrawDiv = React.createElement(
"div",
@@ -1638,7 +1636,7 @@ export default class ExcalidrawView extends TextFileView {
showHoverPreview();
}
},
-/* onKeyUp: (e: any) => {
+ /* onKeyUp: (e: any) => {
this.ctrlKeyDown = e[CTRL_OR_CMD]; //.ctrlKey||e.metaKey;
this.shiftKeyDown = e.shiftKey;
this.altKeyDown = e.altKey;
diff --git a/src/MarkdownPostProcessor.ts b/src/MarkdownPostProcessor.ts
index c7bf7b0..7f20fe0 100644
--- a/src/MarkdownPostProcessor.ts
+++ b/src/MarkdownPostProcessor.ts
@@ -1,483 +1,490 @@
-import { settings } from "cluster";
-import { MarkdownPostProcessorContext, MetadataCache, TFile, Vault } from "obsidian";
-import { CTRL_OR_CMD, RERENDER_EVENT } from "./constants";
-import { EmbeddedFilesLoader } from "./EmbeddedFileLoader";
-import { createPNG, createSVG } from "./ExcalidrawAutomate";
-import { ExportSettings } from "./ExcalidrawView";
-import ExcalidrawPlugin from "./main";
-import { embedFontsInSVG, getIMGFilename, isObsidianThemeDark, splitFolderAndFilename, svgToBase64 } from "./Utils";
-
-interface imgElementAttributes {
- file?: TFile;
- fname: string; //Excalidraw filename
- fwidth: string; //Display width of image
- fheight: string; //Display height of image
- style: string; //css style to apply to IMG element
-}
-
-let plugin: ExcalidrawPlugin;
-let vault: Vault;
-let metadataCache: MetadataCache;
-
-export const initializeMarkdownPostProcessor = (p:ExcalidrawPlugin) => {
- plugin = p;
- vault = p.app.vault;
- metadataCache = p.app.metadataCache;
-}
-
-
-
-/**
- * Generates an img element with the drawing encoded as a base64 SVG or a PNG (depending on settings)
- * @param parts {imgElementAttributes} - display properties of the image
- * @returns {Promise} - the IMG HTML element containing the image
- */
-const getIMG = async (
- imgAttributes: imgElementAttributes,
-): Promise => {
- let file = imgAttributes.file;
- if (!imgAttributes.file) {
- const f = vault.getAbstractFileByPath(imgAttributes.fname);
- if (!(f && f instanceof TFile)) {
- return null;
- }
- file = f;
- }
-
- const exportSettings: ExportSettings = {
- withBackground: plugin.settings.exportWithBackground,
- withTheme: plugin.settings.exportWithTheme,
- };
- const img = createEl("img");
- let style = `max-width:${imgAttributes.fwidth}px !important; width:100%;`;
- if (imgAttributes.fheight) {
- style += `height:${imgAttributes.fheight}px;`;
- }
- img.setAttribute("style", style);
- img.addClass(imgAttributes.style);
-
- const theme = plugin.settings.previewMatchObsidianTheme
- ? isObsidianThemeDark()
- ? "dark"
- : "light"
- : !plugin.settings.exportWithTheme
- ? "light"
- : undefined;
- if (theme) {
- exportSettings.withTheme = true;
- }
- const loader = new EmbeddedFilesLoader(
- plugin,
- theme ? theme === "dark" : undefined,
- );
-
- if (!plugin.settings.displaySVGInPreview) {
- const width = parseInt(imgAttributes.fwidth);
- let scale = 1;
- if (width >= 600) {
- scale = 2;
- }
- if (width >= 1200) {
- scale = 3;
- }
- if (width >= 1800) {
- scale = 4;
- }
- if (width >= 2400) {
- scale = 5;
- }
- const png = await createPNG(
- file.path,
- scale,
- exportSettings,
- loader,
- theme,
- null,
- null,
- [],
- plugin,
- );
- if (!png) {
- return null;
- }
- img.src = URL.createObjectURL(png);
- return img;
- }
- const svgSnapshot = (
- await createSVG(
- file.path,
- true,
- exportSettings,
- loader,
- theme,
- null,
- null,
- [],
- plugin,
- )
- ).outerHTML;
- let svg: SVGSVGElement = null;
- const el = document.createElement("div");
- el.innerHTML = svgSnapshot;
- const firstChild = el.firstChild;
- if (firstChild instanceof SVGSVGElement) {
- svg = firstChild;
- }
- if (!svg) {
- return null;
- }
- svg = embedFontsInSVG(svg,plugin);
- svg.removeAttribute("width");
- svg.removeAttribute("height");
- img.setAttribute("src", svgToBase64(svg.outerHTML));
- return img;
-};
-
-const createImageDiv = async (
- attr: imgElementAttributes,
-): Promise => {
- const img = await getIMG(attr);
- return createDiv(attr.style, (el) => {
- el.append(img);
- el.setAttribute("src", attr.file.path);
- if (attr.fwidth) {
- el.setAttribute("w", attr.fwidth);
- }
- if (attr.fheight) {
- el.setAttribute("h", attr.fheight);
- }
- el.onClickEvent((ev) => {
- if (
- ev.target instanceof Element &&
- ev.target.tagName.toLowerCase() != "img"
- ) {
- return;
- }
- const src = el.getAttribute("src");
- if (src) {
- plugin.openDrawing(
- vault.getAbstractFileByPath(src) as TFile,
- ev[CTRL_OR_CMD],
- );
- } //.ctrlKey||ev.metaKey);
- });
- el.addEventListener(RERENDER_EVENT, async (e) => {
- e.stopPropagation();
- el.empty();
- const img = await getIMG({
- fname: el.getAttribute("src"),
- fwidth: el.getAttribute("w"),
- fheight: el.getAttribute("h"),
- style: el.getAttribute("class"),
- });
- el.append(img);
- });
- });
-};
-
-
-
-const processInternalEmbeds = async (
- embeddedItems:NodeListOf|[HTMLElement],
- ctx: MarkdownPostProcessorContext,
-) => {
- //if not, then we are processing a non-excalidraw file in reading mode
- //in that cases embedded files will be displayed in an .internal-embed container
- const attr: imgElementAttributes = {
- fname: "",
- fheight: "",
- fwidth: "",
- style: "",
- };
- let alt: string;
- let parts;
- let file: TFile;
-
- //Iterating through all the containers to check which one is an excalidraw drawing
- //This is a for loop instead of embeddedItems.forEach() because createImageDiv at the end
- //is awaited, otherwise excalidraw images would not display in the Kanban plugin
- for (const maybeDrawing of embeddedItems) {
- //check to see if the file in the src attribute exists
- attr.fname = maybeDrawing.getAttribute("src");
- file = metadataCache.getFirstLinkpathDest(
- attr.fname?.split("#")[0],
- ctx.sourcePath,
- );
-
- //if the embeddedFile exits and it is an Excalidraw file
- //then lets replace the .internal-embed with the generated PNG or SVG image
- if (file && file instanceof TFile && plugin.isExcalidrawFile(file)) {
- attr.fwidth = maybeDrawing.getAttribute("width")
- ? maybeDrawing.getAttribute("width")
- : plugin.settings.width;
- attr.fheight = maybeDrawing.getAttribute("height");
- alt = maybeDrawing.getAttribute("alt");
- if (alt == attr.fname) {
- alt = "";
- } //when the filename starts with numbers followed by a space Obsidian recognizes the filename as alt-text
- attr.style = "excalidraw-svg";
- if (alt) {
- //for some reason Obsidian renders ![]() in a DIV and ![[]] in a SPAN
- //also the alt-text of the DIV does not include the alt-text of the image
- //thus need to add an additional "|" character when its a SPAN
- if (maybeDrawing.tagName.toLowerCase() == "span") {
- alt = `|${alt}`;
- }
- //1:width, 2:height, 3:style 1 2 3
- parts = alt.match(/[^\|]*\|?(\d*%?)x?(\d*%?)\|?(.*)/);
- attr.fwidth = parts[1] ? parts[1] : plugin.settings.width;
- attr.fheight = parts[2];
- if (parts[3] != attr.fname) {
- attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`;
- }
- }
-
- attr.fname = file?.path;
- attr.file = file;
- const div = await createImageDiv(attr);
- maybeDrawing.parentElement.replaceChild(div, maybeDrawing);
- }
- }
-}
-
-
-const tmpObsidianWYSIWYG = async (
- el: HTMLElement,
- ctx: MarkdownPostProcessorContext,
-) => {
- if (!ctx.frontmatter) {
- return;
- }
- if (!ctx.frontmatter.hasOwnProperty("excalidraw-plugin")) {
- return;
- }
- //@ts-ignore
- if (ctx.remainingNestLevel < 4) {
- return;
- }
- if (!el.querySelector(".frontmatter")) {
- el.style.display = "none";
- return;
- }
- const attr: imgElementAttributes = {
- fname: ctx.sourcePath,
- fheight: "",
- fwidth: plugin.settings.width,
- style: "excalidraw-svg",
- };
-
- attr.file = metadataCache.getFirstLinkpathDest(
- ctx.sourcePath,
- "",
- );
-
- el.empty();
-
- if(!plugin.settings.experimentalLivePreview) {
- el.appendChild(await createImageDiv(attr));
- return;
- }
-
- const div = createDiv();
- el.appendChild(div);
-
- //The timeout gives time for obsidian to attach el to the displayed document
- //Once the element is attached, I can traverse up the dom tree to find .internal-embed
- //If internal embed is not found, it means the that the excalidraw.md file
- //is being rendered in "reading" mode. In that case, the image with the default width
- //specified in setting should be displayed
- //if .internal-embed is found, then contents is replaced with the image using the
- //alt, width, and height attributes of .internal-embed to size and style the image
- setTimeout(async ()=>{
- let internalEmbedDiv:HTMLElement = div;
- while(!internalEmbedDiv.hasClass("internal-embed") && internalEmbedDiv.parentElement) {
- internalEmbedDiv = internalEmbedDiv.parentElement;
- }
-
- if(!internalEmbedDiv.hasClass("internal-embed")) {
- el.empty();
- el.appendChild(await createImageDiv(attr));
- return;
- }
-
- internalEmbedDiv.empty();
-
- const basename = splitFolderAndFilename(attr.fname).basename;
- const setAttr = () => {
- const hasWidth = internalEmbedDiv.getAttribute("width")!=="";
- const hasHeight = internalEmbedDiv.getAttribute("height")!=="";
- if(hasWidth)
- attr.fwidth = internalEmbedDiv.getAttribute("width");
- if(hasHeight)
- attr.fheight = internalEmbedDiv.getAttribute("height");
- const alt = internalEmbedDiv.getAttribute("alt");
- const hasAttr = alt
- && alt !== ""
- && alt !== basename
- && alt !== internalEmbedDiv.getAttribute("src");
- if(hasAttr) {
- //1:width, 2:height, 3:style 1 2 3
- const parts = alt.match(/(\d*%?)x?(\d*%?)\|?(.*)/);
- attr.fwidth = parts[1] ? parts[1] : plugin.settings.width;
- attr.fheight = parts[2];
- if (parts[3] != attr.fname) {
- attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`;
- }
- }
- if(!hasWidth && !hasHeight && !hasAttr) {
- attr.fheight = "";
- attr.fwidth = plugin.settings.width;
- attr.style = "excalidraw-svg";
- }
- }
-
- const createImgElement = async () => {
- setAttr();
- const imgDiv = await createImageDiv(attr);
- internalEmbedDiv.appendChild(imgDiv);
- }
- await createImgElement();
-
- //timer to avoid the image flickering when the user is typing
- let timer:NodeJS.Timeout = null;
- const observer = new MutationObserver((m) => {
- if(!["alt","width","height"].contains(m[0]?.attributeName)) {
- return;
- }
- if(timer) clearTimeout(timer);
- timer = setTimeout(() => {
- timer = null;
- setAttr();
- internalEmbedDiv.empty();
- createImgElement();
- },500);
- });
- observer.observe(internalEmbedDiv, {
- attributes: true, //configure it to listen to attribute changes
- });
- },300);
-};
-
-
-/**
- *
- * @param el
- * @param ctx
- */
-export const markdownPostProcessor = async (
- el: HTMLElement,
- ctx: MarkdownPostProcessorContext,
-) => {
- //check to see if we are rendering in editing mode of live preview
- //if yes, then there should be no .internal-embed containers
- const embeddedItems = el.querySelectorAll(".internal-embed");
- if (embeddedItems.length === 0) {
- tmpObsidianWYSIWYG(el, ctx);
- return;
- }
-
- //If the file being processed is an excalidraw file,
- //then I want to hide all embedded items as these will be
- //transcluded text element or some other transcluded content inside the Excalidraw file
- //in reading mode these elements should be hidden
- if (ctx.frontmatter?.hasOwnProperty("excalidraw-plugin")) {
- el.style.display = "none";
- return;
- }
-
- await processInternalEmbeds( embeddedItems,ctx);
-
-};
-
-/**
- * internal-link quick preview
- * @param e
- * @returns
- */
-export const hoverEvent = (e: any) => {
- if (!e.linktext) {
- plugin.hover.linkText = null;
- return;
- }
- plugin.hover.linkText = e.linktext;
- plugin.hover.sourcePath = e.sourcePath;
-};
-
-//monitoring for div.popover.hover-popover.file-embed.is-loaded to be added to the DOM tree
-export const observer = new MutationObserver(async (m) => {
- if (m.length == 0) {
- return;
- }
- if (!plugin.hover.linkText) {
- return;
- }
- const file = metadataCache.getFirstLinkpathDest(
- plugin.hover.linkText,
- plugin.hover.sourcePath ? plugin.hover.sourcePath : "",
- );
- if (!file) {
- return;
- }
- if (!(file instanceof TFile)) {
- return;
- }
- if (file.extension !== "excalidraw") {
- return;
- }
-
- const svgFileName = getIMGFilename(file.path, "svg");
- const svgFile = vault.getAbstractFileByPath(svgFileName);
- if (svgFile && svgFile instanceof TFile) {
- return;
- } //If auto export SVG or PNG is enabled it will be inserted at the top of the excalidraw file. No need to manually insert hover preview
-
- const pngFileName = getIMGFilename(file.path, "png");
- const pngFile = vault.getAbstractFileByPath(pngFileName);
- if (pngFile && pngFile instanceof TFile) {
- return;
- } //If auto export SVG or PNG is enabled it will be inserted at the top of the excalidraw file. No need to manually insert hover preview
-
- if (!plugin.hover.linkText) {
- return;
- }
- if (m.length != 1) {
- return;
- }
- if (m[0].addedNodes.length != 1) {
- return;
- }
- if (
- //@ts-ignore
- m[0].addedNodes[0].className !=
- "popover hover-popover file-embed is-loaded"
- ) {
- return;
- }
- const node = m[0].addedNodes[0];
- node.empty();
-
- //this div will be on top of original DIV. By stopping the propagation of the click
- //I prevent the default Obsidian feature of openning the link in the native app
- const img = await getIMG({
- file,
- fname: file.path,
- fwidth: "300",
- fheight: null,
- style: "excalidraw-svg",
- });
- const div = createDiv("", async (el) => {
- el.appendChild(img);
- el.setAttribute("src", file.path);
- el.onClickEvent((ev) => {
- ev.stopImmediatePropagation();
- const src = el.getAttribute("src");
- if (src) {
- plugin.openDrawing(
- vault.getAbstractFileByPath(src) as TFile,
- ev[CTRL_OR_CMD],
- );
- } //.ctrlKey||ev.metaKey);
- });
- });
- node.appendChild(div);
-});
+import {
+ MarkdownPostProcessorContext,
+ MetadataCache,
+ TFile,
+ Vault,
+} from "obsidian";
+import { CTRL_OR_CMD, RERENDER_EVENT } from "./constants";
+import { EmbeddedFilesLoader } from "./EmbeddedFileLoader";
+import { createPNG, createSVG } from "./ExcalidrawAutomate";
+import { ExportSettings } from "./ExcalidrawView";
+import ExcalidrawPlugin from "./main";
+import {
+ embedFontsInSVG,
+ getIMGFilename,
+ isObsidianThemeDark,
+ splitFolderAndFilename,
+ svgToBase64,
+} from "./Utils";
+
+interface imgElementAttributes {
+ file?: TFile;
+ fname: string; //Excalidraw filename
+ fwidth: string; //Display width of image
+ fheight: string; //Display height of image
+ style: string; //css style to apply to IMG element
+}
+
+let plugin: ExcalidrawPlugin;
+let vault: Vault;
+let metadataCache: MetadataCache;
+
+export const initializeMarkdownPostProcessor = (p: ExcalidrawPlugin) => {
+ plugin = p;
+ vault = p.app.vault;
+ metadataCache = p.app.metadataCache;
+};
+
+/**
+ * Generates an img element with the drawing encoded as a base64 SVG or a PNG (depending on settings)
+ * @param parts {imgElementAttributes} - display properties of the image
+ * @returns {Promise} - the IMG HTML element containing the image
+ */
+const getIMG = async (
+ imgAttributes: imgElementAttributes,
+): Promise => {
+ let file = imgAttributes.file;
+ if (!imgAttributes.file) {
+ const f = vault.getAbstractFileByPath(imgAttributes.fname);
+ if (!(f && f instanceof TFile)) {
+ return null;
+ }
+ file = f;
+ }
+
+ const exportSettings: ExportSettings = {
+ withBackground: plugin.settings.exportWithBackground,
+ withTheme: plugin.settings.exportWithTheme,
+ };
+ const img = createEl("img");
+ let style = `max-width:${imgAttributes.fwidth}px !important; width:100%;`;
+ if (imgAttributes.fheight) {
+ style += `height:${imgAttributes.fheight}px;`;
+ }
+ img.setAttribute("style", style);
+ img.addClass(imgAttributes.style);
+
+ const theme = plugin.settings.previewMatchObsidianTheme
+ ? isObsidianThemeDark()
+ ? "dark"
+ : "light"
+ : !plugin.settings.exportWithTheme
+ ? "light"
+ : undefined;
+ if (theme) {
+ exportSettings.withTheme = true;
+ }
+ const loader = new EmbeddedFilesLoader(
+ plugin,
+ theme ? theme === "dark" : undefined,
+ );
+
+ if (!plugin.settings.displaySVGInPreview) {
+ const width = parseInt(imgAttributes.fwidth);
+ let scale = 1;
+ if (width >= 600) {
+ scale = 2;
+ }
+ if (width >= 1200) {
+ scale = 3;
+ }
+ if (width >= 1800) {
+ scale = 4;
+ }
+ if (width >= 2400) {
+ scale = 5;
+ }
+ const png = await createPNG(
+ file.path,
+ scale,
+ exportSettings,
+ loader,
+ theme,
+ null,
+ null,
+ [],
+ plugin,
+ );
+ if (!png) {
+ return null;
+ }
+ img.src = URL.createObjectURL(png);
+ return img;
+ }
+ const svgSnapshot = (
+ await createSVG(
+ file.path,
+ true,
+ exportSettings,
+ loader,
+ theme,
+ null,
+ null,
+ [],
+ plugin,
+ )
+ ).outerHTML;
+ let svg: SVGSVGElement = null;
+ const el = document.createElement("div");
+ el.innerHTML = svgSnapshot;
+ const firstChild = el.firstChild;
+ if (firstChild instanceof SVGSVGElement) {
+ svg = firstChild;
+ }
+ if (!svg) {
+ return null;
+ }
+ svg = embedFontsInSVG(svg, plugin);
+ svg.removeAttribute("width");
+ svg.removeAttribute("height");
+ img.setAttribute("src", svgToBase64(svg.outerHTML));
+ return img;
+};
+
+const createImageDiv = async (
+ attr: imgElementAttributes,
+): Promise => {
+ const img = await getIMG(attr);
+ return createDiv(attr.style, (el) => {
+ el.append(img);
+ el.setAttribute("src", attr.file.path);
+ if (attr.fwidth) {
+ el.setAttribute("w", attr.fwidth);
+ }
+ if (attr.fheight) {
+ el.setAttribute("h", attr.fheight);
+ }
+ el.onClickEvent((ev) => {
+ if (
+ ev.target instanceof Element &&
+ ev.target.tagName.toLowerCase() != "img"
+ ) {
+ return;
+ }
+ const src = el.getAttribute("src");
+ if (src) {
+ plugin.openDrawing(
+ vault.getAbstractFileByPath(src) as TFile,
+ ev[CTRL_OR_CMD],
+ );
+ } //.ctrlKey||ev.metaKey);
+ });
+ el.addEventListener(RERENDER_EVENT, async (e) => {
+ e.stopPropagation();
+ el.empty();
+ const img = await getIMG({
+ fname: el.getAttribute("src"),
+ fwidth: el.getAttribute("w"),
+ fheight: el.getAttribute("h"),
+ style: el.getAttribute("class"),
+ });
+ el.append(img);
+ });
+ });
+};
+
+const processInternalEmbeds = async (
+ embeddedItems: NodeListOf | [HTMLElement],
+ ctx: MarkdownPostProcessorContext,
+) => {
+ //if not, then we are processing a non-excalidraw file in reading mode
+ //in that cases embedded files will be displayed in an .internal-embed container
+ const attr: imgElementAttributes = {
+ fname: "",
+ fheight: "",
+ fwidth: "",
+ style: "",
+ };
+ let alt: string;
+ let parts;
+ let file: TFile;
+
+ //Iterating through all the containers to check which one is an excalidraw drawing
+ //This is a for loop instead of embeddedItems.forEach() because createImageDiv at the end
+ //is awaited, otherwise excalidraw images would not display in the Kanban plugin
+ for (const maybeDrawing of embeddedItems) {
+ //check to see if the file in the src attribute exists
+ attr.fname = maybeDrawing.getAttribute("src");
+ file = metadataCache.getFirstLinkpathDest(
+ attr.fname?.split("#")[0],
+ ctx.sourcePath,
+ );
+
+ //if the embeddedFile exits and it is an Excalidraw file
+ //then lets replace the .internal-embed with the generated PNG or SVG image
+ if (file && file instanceof TFile && plugin.isExcalidrawFile(file)) {
+ attr.fwidth = maybeDrawing.getAttribute("width")
+ ? maybeDrawing.getAttribute("width")
+ : plugin.settings.width;
+ attr.fheight = maybeDrawing.getAttribute("height");
+ alt = maybeDrawing.getAttribute("alt");
+ if (alt == attr.fname) {
+ alt = "";
+ } //when the filename starts with numbers followed by a space Obsidian recognizes the filename as alt-text
+ attr.style = "excalidraw-svg";
+ if (alt) {
+ //for some reason Obsidian renders ![]() in a DIV and ![[]] in a SPAN
+ //also the alt-text of the DIV does not include the alt-text of the image
+ //thus need to add an additional "|" character when its a SPAN
+ if (maybeDrawing.tagName.toLowerCase() == "span") {
+ alt = `|${alt}`;
+ }
+ //1:width, 2:height, 3:style 1 2 3
+ parts = alt.match(/[^\|]*\|?(\d*%?)x?(\d*%?)\|?(.*)/);
+ attr.fwidth = parts[1] ? parts[1] : plugin.settings.width;
+ attr.fheight = parts[2];
+ if (parts[3] != attr.fname) {
+ attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`;
+ }
+ }
+
+ attr.fname = file?.path;
+ attr.file = file;
+ const div = await createImageDiv(attr);
+ maybeDrawing.parentElement.replaceChild(div, maybeDrawing);
+ }
+ }
+};
+
+const tmpObsidianWYSIWYG = async (
+ el: HTMLElement,
+ ctx: MarkdownPostProcessorContext,
+) => {
+ if (!ctx.frontmatter) {
+ return;
+ }
+ if (!ctx.frontmatter.hasOwnProperty("excalidraw-plugin")) {
+ return;
+ }
+ //@ts-ignore
+ if (ctx.remainingNestLevel < 4) {
+ return;
+ }
+ if (!el.querySelector(".frontmatter")) {
+ el.style.display = "none";
+ return;
+ }
+ const attr: imgElementAttributes = {
+ fname: ctx.sourcePath,
+ fheight: "",
+ fwidth: plugin.settings.width,
+ style: "excalidraw-svg",
+ };
+
+ attr.file = metadataCache.getFirstLinkpathDest(ctx.sourcePath, "");
+
+ el.empty();
+
+ if (!plugin.settings.experimentalLivePreview) {
+ el.appendChild(await createImageDiv(attr));
+ return;
+ }
+
+ const div = createDiv();
+ el.appendChild(div);
+
+ //The timeout gives time for obsidian to attach el to the displayed document
+ //Once the element is attached, I can traverse up the dom tree to find .internal-embed
+ //If internal embed is not found, it means the that the excalidraw.md file
+ //is being rendered in "reading" mode. In that case, the image with the default width
+ //specified in setting should be displayed
+ //if .internal-embed is found, then contents is replaced with the image using the
+ //alt, width, and height attributes of .internal-embed to size and style the image
+ setTimeout(async () => {
+ let internalEmbedDiv: HTMLElement = div;
+ while (
+ !internalEmbedDiv.hasClass("internal-embed") &&
+ internalEmbedDiv.parentElement
+ ) {
+ internalEmbedDiv = internalEmbedDiv.parentElement;
+ }
+
+ if (!internalEmbedDiv.hasClass("internal-embed")) {
+ el.empty();
+ el.appendChild(await createImageDiv(attr));
+ return;
+ }
+
+ internalEmbedDiv.empty();
+
+ const basename = splitFolderAndFilename(attr.fname).basename;
+ const setAttr = () => {
+ const hasWidth = internalEmbedDiv.getAttribute("width") !== "";
+ const hasHeight = internalEmbedDiv.getAttribute("height") !== "";
+ if (hasWidth) {
+ attr.fwidth = internalEmbedDiv.getAttribute("width");
+ }
+ if (hasHeight) {
+ attr.fheight = internalEmbedDiv.getAttribute("height");
+ }
+ const alt = internalEmbedDiv.getAttribute("alt");
+ const hasAttr =
+ alt &&
+ alt !== "" &&
+ alt !== basename &&
+ alt !== internalEmbedDiv.getAttribute("src");
+ if (hasAttr) {
+ //1:width, 2:height, 3:style 1 2 3
+ const parts = alt.match(/(\d*%?)x?(\d*%?)\|?(.*)/);
+ attr.fwidth = parts[1] ? parts[1] : plugin.settings.width;
+ attr.fheight = parts[2];
+ if (parts[3] != attr.fname) {
+ attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`;
+ }
+ }
+ if (!hasWidth && !hasHeight && !hasAttr) {
+ attr.fheight = "";
+ attr.fwidth = plugin.settings.width;
+ attr.style = "excalidraw-svg";
+ }
+ };
+
+ const createImgElement = async () => {
+ setAttr();
+ const imgDiv = await createImageDiv(attr);
+ internalEmbedDiv.appendChild(imgDiv);
+ };
+ await createImgElement();
+
+ //timer to avoid the image flickering when the user is typing
+ let timer: NodeJS.Timeout = null;
+ const observer = new MutationObserver((m) => {
+ if (!["alt", "width", "height"].contains(m[0]?.attributeName)) {
+ return;
+ }
+ if (timer) {
+ clearTimeout(timer);
+ }
+ timer = setTimeout(() => {
+ timer = null;
+ setAttr();
+ internalEmbedDiv.empty();
+ createImgElement();
+ }, 500);
+ });
+ observer.observe(internalEmbedDiv, {
+ attributes: true, //configure it to listen to attribute changes
+ });
+ }, 300);
+};
+
+/**
+ *
+ * @param el
+ * @param ctx
+ */
+export const markdownPostProcessor = async (
+ el: HTMLElement,
+ ctx: MarkdownPostProcessorContext,
+) => {
+ //check to see if we are rendering in editing mode of live preview
+ //if yes, then there should be no .internal-embed containers
+ const embeddedItems = el.querySelectorAll(".internal-embed");
+ if (embeddedItems.length === 0) {
+ tmpObsidianWYSIWYG(el, ctx);
+ return;
+ }
+
+ //If the file being processed is an excalidraw file,
+ //then I want to hide all embedded items as these will be
+ //transcluded text element or some other transcluded content inside the Excalidraw file
+ //in reading mode these elements should be hidden
+ if (ctx.frontmatter?.hasOwnProperty("excalidraw-plugin")) {
+ el.style.display = "none";
+ return;
+ }
+
+ await processInternalEmbeds(embeddedItems, ctx);
+};
+
+/**
+ * internal-link quick preview
+ * @param e
+ * @returns
+ */
+export const hoverEvent = (e: any) => {
+ if (!e.linktext) {
+ plugin.hover.linkText = null;
+ return;
+ }
+ plugin.hover.linkText = e.linktext;
+ plugin.hover.sourcePath = e.sourcePath;
+};
+
+//monitoring for div.popover.hover-popover.file-embed.is-loaded to be added to the DOM tree
+export const observer = new MutationObserver(async (m) => {
+ if (m.length == 0) {
+ return;
+ }
+ if (!plugin.hover.linkText) {
+ return;
+ }
+ const file = metadataCache.getFirstLinkpathDest(
+ plugin.hover.linkText,
+ plugin.hover.sourcePath ? plugin.hover.sourcePath : "",
+ );
+ if (!file) {
+ return;
+ }
+ if (!(file instanceof TFile)) {
+ return;
+ }
+ if (file.extension !== "excalidraw") {
+ return;
+ }
+
+ const svgFileName = getIMGFilename(file.path, "svg");
+ const svgFile = vault.getAbstractFileByPath(svgFileName);
+ if (svgFile && svgFile instanceof TFile) {
+ return;
+ } //If auto export SVG or PNG is enabled it will be inserted at the top of the excalidraw file. No need to manually insert hover preview
+
+ const pngFileName = getIMGFilename(file.path, "png");
+ const pngFile = vault.getAbstractFileByPath(pngFileName);
+ if (pngFile && pngFile instanceof TFile) {
+ return;
+ } //If auto export SVG or PNG is enabled it will be inserted at the top of the excalidraw file. No need to manually insert hover preview
+
+ if (!plugin.hover.linkText) {
+ return;
+ }
+ if (m.length != 1) {
+ return;
+ }
+ if (m[0].addedNodes.length != 1) {
+ return;
+ }
+ if (
+ //@ts-ignore
+ m[0].addedNodes[0].className != "popover hover-popover file-embed is-loaded"
+ ) {
+ return;
+ }
+ const node = m[0].addedNodes[0];
+ node.empty();
+
+ //this div will be on top of original DIV. By stopping the propagation of the click
+ //I prevent the default Obsidian feature of openning the link in the native app
+ const img = await getIMG({
+ file,
+ fname: file.path,
+ fwidth: "300",
+ fheight: null,
+ style: "excalidraw-svg",
+ });
+ const div = createDiv("", async (el) => {
+ el.appendChild(img);
+ el.setAttribute("src", file.path);
+ el.onClickEvent((ev) => {
+ ev.stopImmediatePropagation();
+ const src = el.getAttribute("src");
+ if (src) {
+ plugin.openDrawing(
+ vault.getAbstractFileByPath(src) as TFile,
+ ev[CTRL_OR_CMD],
+ );
+ } //.ctrlKey||ev.metaKey);
+ });
+ });
+ node.appendChild(div);
+});
diff --git a/src/Prompt.ts b/src/Prompt.ts
index 1db44c5..88df522 100644
--- a/src/Prompt.ts
+++ b/src/Prompt.ts
@@ -5,6 +5,7 @@ import {
TextComponent,
FuzzyMatch,
FuzzySuggestModal,
+ Instruction,
} from "obsidian";
export class Prompt extends Modal {
@@ -211,25 +212,43 @@ export class GenericInputPrompt extends Modal {
}
}
-export class GenericSuggester extends FuzzySuggestModal {
- private resolvePromise: (value: string) => void;
+export class GenericSuggester extends FuzzySuggestModal {
+ private resolvePromise: (value: any) => void;
private rejectPromise: (reason?: any) => void;
- public promise: Promise;
+ public promise: Promise;
private resolved: boolean;
- public static Suggest(app: App, displayItems: string[], items: string[]) {
- const newSuggester = new GenericSuggester(app, displayItems, items);
+ public static Suggest(
+ app: App,
+ displayItems: string[],
+ items: string[],
+ hint?: string,
+ instructions?: Instruction[],
+ ) {
+ const newSuggester = new GenericSuggester(
+ app,
+ displayItems,
+ items,
+ hint,
+ instructions,
+ );
return newSuggester.promise;
}
public constructor(
app: App,
private displayItems: string[],
- private items: string[],
+ private items: any[],
+ private hint?: string,
+ private instructions?: Instruction[],
) {
super(app);
- this.limit =20;
- this.promise = new Promise((resolve, reject) => {
+ this.limit = 20;
+ this.setPlaceholder(this.hint ?? "");
+ if (instructions) {
+ this.setInstructions(this.instructions);
+ }
+ this.promise = new Promise((resolve, reject) => {
this.resolvePromise = resolve;
this.rejectPromise = reject;
});
@@ -241,7 +260,7 @@ export class GenericSuggester extends FuzzySuggestModal {
return this.displayItems[this.items.indexOf(item)];
}
- getItems(): string[] {
+ getItems(): any[] {
return this.items;
}
@@ -250,16 +269,15 @@ export class GenericSuggester extends FuzzySuggestModal {
super.selectSuggestion(value, evt);
}
- onChooseItem(item: string): void {
+ onChooseItem(item: any): void {
this.resolved = true;
this.resolvePromise(item);
}
onClose() {
super.onClose();
-
if (!this.resolved) {
- this.rejectPromise("no input given.");
+ this.rejectPromise(this.inputEl.value);
}
}
}
diff --git a/src/ScriptInstallPrompt.ts b/src/ScriptInstallPrompt.ts
index 9106cb5..c3bcc63 100644
--- a/src/ScriptInstallPrompt.ts
+++ b/src/ScriptInstallPrompt.ts
@@ -1,6 +1,4 @@
-import { App, MarkdownRenderer, Modal, Notice, request } from "obsidian";
-import { Url } from "url";
-import { t } from "./lang/helpers";
+import { MarkdownRenderer, Modal, Notice, request } from "obsidian";
import ExcalidrawPlugin from "./main";
import { errorlog, log } from "./Utils";
@@ -18,10 +16,13 @@ export class ScriptInstallPrompt extends Modal {
this.containerEl.classList.add("excalidraw-scriptengine-install");
try {
const source = await request({ url: URL });
- if(!source) {
- new Notice("Error opening the Excalidraw Script Store page. " +
- "Please double check that you can access the website. " +
- "I've logged the link in developer console (press CTRL+SHIFT+i)",5000);
+ if (!source) {
+ new Notice(
+ "Error opening the Excalidraw Script Store page. " +
+ "Please double check that you can access the website. " +
+ "I've logged the link in developer console (press CTRL+SHIFT+i)",
+ 5000,
+ );
log(URL);
this.close();
return;
@@ -32,9 +33,11 @@ export class ScriptInstallPrompt extends Modal {
"",
this.plugin,
);
- this.contentEl.querySelectorAll("h1[data-heading],h2[data-heading],h3[data-heading]").forEach((el) => {
- el.setAttribute("id", el.getAttribute("data-heading"));
- });
+ this.contentEl
+ .querySelectorAll("h1[data-heading],h2[data-heading],h3[data-heading]")
+ .forEach((el) => {
+ el.setAttribute("id", el.getAttribute("data-heading"));
+ });
this.contentEl.querySelectorAll("a.internal-link").forEach((el) => {
el.removeAttribute("target");
});
diff --git a/src/Scripts.ts b/src/Scripts.ts
index 50da155..a8f219b 100644
--- a/src/Scripts.ts
+++ b/src/Scripts.ts
@@ -1,5 +1,4 @@
-import { sub } from "@zsviczian/excalidraw/types/ga";
-import { App, TAbstractFile, TFile } from "obsidian";
+import { App, Instruction, TAbstractFile, TFile } from "obsidian";
import { PLUGIN_ID, VIEW_TYPE_EXCALIDRAW } from "./constants";
import ExcalidrawView from "./ExcalidrawView";
import ExcalidrawPlugin from "./main";
@@ -65,14 +64,16 @@ export class ScriptEngine {
if (this.scriptPath === this.plugin.settings.scriptFolderPath) {
return;
}
- if(this.scriptPath) this.unloadScripts();
+ if (this.scriptPath) {
+ this.unloadScripts();
+ }
this.loadScripts();
}
loadScripts() {
const app = this.plugin.app;
this.scriptPath = this.plugin.settings.scriptFolderPath;
- if(!app.vault.getAbstractFileByPath(this.scriptPath)) {
+ if (!app.vault.getAbstractFileByPath(this.scriptPath)) {
this.scriptPath = null;
return;
}
@@ -82,10 +83,10 @@ export class ScriptEngine {
scripts.forEach((f) => this.loadScript(f));
}
- getScriptName(f:TFile|string):string {
+ getScriptName(f: TFile | string): string {
let basename = "";
let path = "";
- if(f instanceof TFile) {
+ if (f instanceof TFile) {
basename = f.basename;
path = f.path;
} else {
@@ -93,9 +94,7 @@ export class ScriptEngine {
path = f;
}
- const subpath = path.split(
- `${this.scriptPath}/`,
- )[1];
+ const subpath = path.split(`${this.scriptPath}/`)[1];
const lastSlash = subpath.lastIndexOf("/");
if (lastSlash > -1) {
return subpath.substring(0, lastSlash + 1) + basename;
@@ -162,12 +161,26 @@ export class ScriptEngine {
//https://stackoverflow.com/questions/45381204/get-asyncfunction-constructor-in-typescript changed tsconfig to es2017
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
- const result = await new AsyncFunction("ea", "utils", script)(this.plugin.ea, {
- inputPrompt: (header: string, placeholder?: string, value?: string) =>
- ScriptEngine.inputPrompt(this.plugin.app, header, placeholder, value),
- suggester: (displayItems: string[], items: string[]) =>
- ScriptEngine.suggester(this.plugin.app, displayItems, items),
- });
+ const result = await new AsyncFunction("ea", "utils", script)(
+ this.plugin.ea,
+ {
+ inputPrompt: (header: string, placeholder?: string, value?: string) =>
+ ScriptEngine.inputPrompt(this.plugin.app, header, placeholder, value),
+ suggester: (
+ displayItems: string[],
+ items: any[],
+ hint?: string,
+ instructions?: Instruction[],
+ ) =>
+ ScriptEngine.suggester(
+ this.plugin.app,
+ displayItems,
+ items,
+ hint,
+ instructions,
+ ),
+ },
+ );
this.plugin.ea.activeScript = null;
return result;
}
@@ -188,11 +201,19 @@ export class ScriptEngine {
public static async suggester(
app: App,
displayItems: string[],
- items: string[],
+ items: any[],
+ hint?: string,
+ instructions?: Instruction[],
) {
try {
- return await GenericSuggester.Suggest(app, displayItems, items);
- } catch {
+ return await GenericSuggester.Suggest(
+ app,
+ displayItems,
+ items,
+ hint,
+ instructions,
+ );
+ } catch (e) {
return undefined;
}
}
diff --git a/src/Utils.ts b/src/Utils.ts
index 750d7e8..f6ea013 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -11,7 +11,12 @@ import {
} from "obsidian";
import { Random } from "roughjs/bin/math";
import { DataURL, Zoom } from "@zsviczian/excalidraw/types/types";
-import { CASCADIA_FONT, REG_BLOCK_REF_CLEAN, VIRGIL_FONT,PLUGIN_ID } from "./constants";
+import {
+ CASCADIA_FONT,
+ REG_BLOCK_REF_CLEAN,
+ VIRGIL_FONT,
+ PLUGIN_ID,
+} from "./constants";
import ExcalidrawPlugin from "./main";
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/element/types";
import { ExportSettings } from "./ExcalidrawView";
@@ -28,41 +33,44 @@ declare module "obsidian" {
}
}
-
let versionUpdateChecked = false;
-export const checkExcalidrawVersion = async (app:App) => {
- if(versionUpdateChecked) return;
+export const checkExcalidrawVersion = async (app: App) => {
+ if (versionUpdateChecked) {
+ return;
+ }
versionUpdateChecked = true;
//@ts-ignore
const manifest = app.plugins.manifests[PLUGIN_ID];
-
+
try {
const gitAPIrequest = async () => {
- return JSON.parse(await request({
- url: `https://api.github.com/repos/zsviczian/obsidian-excalidraw-plugin/releases?per_page=5&page=1`,
- }))
- }
+ return JSON.parse(
+ await request({
+ url: `https://api.github.com/repos/zsviczian/obsidian-excalidraw-plugin/releases?per_page=5&page=1`,
+ }),
+ );
+ };
- const latestVersion = ((await gitAPIrequest())
- .map((el:any) => {
+ const latestVersion = (await gitAPIrequest())
+ .map((el: any) => {
return {
version: el.tag_name,
published: new Date(el.published_at),
};
})
- .filter((el:any) => el.version.match(/^\d+\.\d+\.\d+$/))
- .sort((el1:any,el2:any)=>el2.published-el1.published))[0].version;
+ .filter((el: any) => el.version.match(/^\d+\.\d+\.\d+$/))
+ .sort((el1: any, el2: any) => el2.published - el1.published)[0].version;
- if(latestVersion>manifest.version) {
- new Notice(`A newer version of Excalidraw is available in Community Plugins.\n\nYou are using ${manifest.version}.\nThe latest is ${latestVersion}`);
+ if (latestVersion > manifest.version) {
+ new Notice(
+ `A newer version of Excalidraw is available in Community Plugins.\n\nYou are using ${manifest.version}.\nThe latest is ${latestVersion}`,
+ );
}
- } catch(e) {
- errorlog({where:"Utils/checkExcalidrawVersion", error:e});
+ } catch (e) {
+ errorlog({ where: "Utils/checkExcalidrawVersion", error: e });
}
- setTimeout(()=>versionUpdateChecked=false,28800000);//reset after 8 hours
-}
-
-
+ setTimeout(() => (versionUpdateChecked = false), 28800000); //reset after 8 hours
+};
/**
* Splits a full path including a folderpath and a filename into separate folderpath and filename components
@@ -319,36 +327,30 @@ export const getDataURL = async (
};
export const getFontDataURL = async (
- app:App,
+ app: App,
fontFileName: string,
sourcePath: string,
- name?:string
-):Promise<{fontDef: string, fontName: string, dataURL: string}> => {
- let fontDef:string = "";
+ name?: string,
+): Promise<{ fontDef: string; fontName: string; dataURL: string }> => {
+ let fontDef: string = "";
let fontName = "";
let dataURL = "";
- const f = app.metadataCache.getFirstLinkpathDest(
- fontFileName,
- sourcePath,
- );
+ const f = app.metadataCache.getFirstLinkpathDest(fontFileName, sourcePath);
if (f) {
const ab = await app.vault.readBinary(f);
const mimeType = f.extension.startsWith("woff")
? "application/font-woff"
: "font/truetype";
- fontName = name??f.basename;
- dataURL = await getDataURL(
- ab,
- mimeType,
- );
- fontDef = ` @font-face {font-family: "${fontName}";src: url("${
- dataURL
- }") format("${f.extension === "ttf" ? "truetype" : f.extension}");}`;
+ fontName = name ?? f.basename;
+ dataURL = await getDataURL(ab, mimeType);
+ fontDef = ` @font-face {font-family: "${fontName}";src: url("${dataURL}") format("${
+ f.extension === "ttf" ? "truetype" : f.extension
+ }");}`;
const split = fontDef.split(";base64,", 2);
fontDef = `${split[0]};charset=utf-8;base64,${split[1]}`;
- }
- return {fontDef,fontName,dataURL};
-}
+ }
+ return { fontDef, fontName, dataURL };
+};
export const svgToBase64 = (svg: string): string => {
return `data:image/svg+xml;base64,${btoa(
@@ -393,7 +395,9 @@ export const getAttachmentsFolderAndFilePath = async (
await checkAndCreateFolder(app.vault, folder);
return {
folder,
- filepath: normalizePath(folder===""?newFileName:`${folder}/${newFileName}`),
+ filepath: normalizePath(
+ folder === "" ? newFileName : `${folder}/${newFileName}`,
+ ),
};
};
@@ -448,7 +452,10 @@ export const getPNG = async (
}
};
-export const embedFontsInSVG = (svg: SVGSVGElement, plugin: ExcalidrawPlugin): SVGSVGElement => {
+export const embedFontsInSVG = (
+ svg: SVGSVGElement,
+ plugin: ExcalidrawPlugin,
+): SVGSVGElement => {
//replace font references with base64 fonts
const includesVirgil =
svg.querySelector("text[font-family^='Virgil']") != null;
diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts
index 4171d7f..5dbaa05 100644
--- a/src/lang/locale/en.ts
+++ b/src/lang/locale/en.ts
@@ -9,9 +9,12 @@ export default {
// main.ts
INSTALL_SCRIPT: "Install this script",
UPDATE_SCRIPT: "An update is available - Click to install",
- CHECKING_SCRIPT: "Checking if a newer version is available - Click to reinstall now",
- UNABLETOCHECK_SCRIPT: "Update check was unsuccessful - Click to reinstall now",
- UPTODATE_SCRIPT: "Script is installed and up to date - Click to reinstall now",
+ CHECKING_SCRIPT:
+ "Checking if a newer version is available - Click to reinstall now",
+ UNABLETOCHECK_SCRIPT:
+ "Update check was unsuccessful - Click to reinstall now",
+ UPTODATE_SCRIPT:
+ "Script is installed and up to date - Click to reinstall now",
OPEN_AS_EXCALIDRAW: "Open as Excalidraw Drawing",
TOGGLE_MODE: "Toggle between Excalidraw and Markdown mode",
CONVERT_NOTE_TO_EXCALIDRAW: "Convert empty note to Excalidraw Drawing",
@@ -73,10 +76,12 @@ export default {
FOLDER_NAME: "Excalidraw folder",
FOLDER_DESC:
"Default location for new drawings. If empty, drawings will be created in the Vault root.",
- FOLDER_EMBED_NAME: "Use Excalidraw folder when embedding a drawing into the active document",
- FOLDER_EMBED_DESC: "Define which folder to place the newly inserted drawing into " +
- "when using the command palette action: 'Create a new drawing and embed into active document'. " +
- "ON: Use Excalidraw folder; OFF: use attachments folder defined in Obsidian settings",
+ FOLDER_EMBED_NAME:
+ "Use Excalidraw folder when embedding a drawing into the active document",
+ FOLDER_EMBED_DESC:
+ "Define which folder to place the newly inserted drawing into " +
+ "when using the command palette action: 'Create a new drawing and embed into active document'. " +
+ "ON: Use Excalidraw folder; OFF: use attachments folder defined in Obsidian settings",
TEMPLATE_NAME: "Excalidraw template file",
TEMPLATE_DESC:
"Full filepath to the Excalidraw template. " +
@@ -106,9 +111,10 @@ export default {
FILENAME_PREFIX_NAME: "Filename prefix",
FILENAME_PREFIX_DESC: "The first part of the filename",
FILENAME_PREFIX_EMBED_NAME: "Filename prefix for embedded files",
- FILENAME_PREFIX_EMBED_DESC: "Name of the newly inserted drawing should start with the name of the active note "+
- "when using the command palette action: 'Create a new drawing and embed into active document'. " +
- "On: yes, Off: not",
+ FILENAME_PREFIX_EMBED_DESC:
+ "Name of the newly inserted drawing should start with the name of the active note " +
+ "when using the command palette action: 'Create a new drawing and embed into active document'. " +
+ "On: yes, Off: not",
FILENAME_DATE_NAME: "Filename date",
FILENAME_DATE_DESC: "The second part of the filename",
/*SVG_IN_MD_NAME: "SVG Snapshot to markdown file",
@@ -280,18 +286,21 @@ export default {
FILETAG_DESC: "The text or emojii to display as type indicator.",
INSERT_EMOJI: "Insert an emoji",
LIVEPREVIEW_NAME: "Immersive image embedding in live preview editing mode",
- LIVEPREVIEW_DESC: "Turn this on to support image embedding styles such as ![[drawing|width|style]] in live preview editing mode. " +
- "The setting will not effect the currently open documents. You need close the open documents and re-open them for the change " +
- "to take effect.",
+ LIVEPREVIEW_DESC:
+ "Turn this on to support image embedding styles such as ![[drawing|width|style]] in live preview editing mode. " +
+ "The setting will not effect the currently open documents. You need close the open documents and re-open them for the change " +
+ "to take effect.",
ENABLE_FOURTH_FONT_NAME: "Enable fourth font option",
- ENABLE_FOURTH_FONT_DESC: "By turning this on, you will see a fourth font button on the properties panel for text elements. " +
- "Files that use this fourth font will (partly) lose their paltform independence. " +
- "Depending on the cutom font set in settings, they will look differently when loaded in another vault, or at a later time. " +
- "Also the 4th font will display as system default font on excalidraw.com, or other Excalidraw versions.",
+ ENABLE_FOURTH_FONT_DESC:
+ "By turning this on, you will see a fourth font button on the properties panel for text elements. " +
+ "Files that use this fourth font will (partly) lose their paltform independence. " +
+ "Depending on the cutom font set in settings, they will look differently when loaded in another vault, or at a later time. " +
+ "Also the 4th font will display as system default font on excalidraw.com, or other Excalidraw versions.",
FOURTH_FONT_NAME: "Forth font file",
- FOURTH_FONT_DESC: "Select a .ttf, .woff or .woff2 font file from your vault to use as the fourth font. " +
- "If no file is selected excalidraw will use the Virgil font by default.",
-
+ FOURTH_FONT_DESC:
+ "Select a .ttf, .woff or .woff2 font file from your vault to use as the fourth font. " +
+ "If no file is selected excalidraw will use the Virgil font by default.",
+ SCRIPT_SETTINGS_HEAD: "Settings for installed Scripts",
//openDrawings.ts
SELECT_FILE: "Select a file then press enter.",
diff --git a/src/lang/locale/zh-cn.ts b/src/lang/locale/zh-cn.ts
index 6b77ca8..8a53393 100644
--- a/src/lang/locale/zh-cn.ts
+++ b/src/lang/locale/zh-cn.ts
@@ -21,16 +21,13 @@ export default {
CONVERT_FILE_REPLACE_EXT: "*.excalidraw 格式 => *.md (兼容 Logseq) 格式",
DOWNLOAD_LIBRARY: "导出 stencil 库为 *.excalidrawlib 文件",
OPEN_EXISTING_NEW_PANE: "在新面板中打开已有的绘图",
- OPEN_EXISTING_ACTIVE_PANE:
- "在当前面板中打开已有的绘图",
+ OPEN_EXISTING_ACTIVE_PANE: "在当前面板中打开已有的绘图",
TRANSCLUDE: "嵌入绘图到该文档",
TRANSCLUDE_MOST_RECENT: "嵌入最近编辑的绘图到该文档",
NEW_IN_NEW_PANE: "在新面板中新建绘图",
NEW_IN_ACTIVE_PANE: "在当前面板中新建绘图",
- NEW_IN_NEW_PANE_EMBED:
- "在新面板中新建绘图,并嵌入到当前文档",
- NEW_IN_ACTIVE_PANE_EMBED:
- "在当前面板中新建绘图,并嵌入到当前文档",
+ NEW_IN_NEW_PANE_EMBED: "在新面板中新建绘图,并嵌入到当前文档",
+ NEW_IN_ACTIVE_PANE_EMBED: "在当前面板中新建绘图,并嵌入到当前文档",
EXPORT_SVG: "导出 SVG 文件到当前目录",
EXPORT_PNG: "导出 PNG 文件到当前目录",
TOGGLE_LOCK: "切换文本元素的原文/预览模式",
@@ -38,16 +35,17 @@ export default {
INSERT_LINK: "插入链接到该绘图",
INSERT_IMAGE: "插入库文件夹里的图像到该绘图",
INSERT_MD: "将库文件夹里的 Markdown 文件以图像形式嵌入到该绘图",
- INSERT_LATEX:
- "插入 LaTeX 公式到该绘图",
- ENTER_LATEX: "输入 LaTeX 表达式(示例: \\binom{n}{k} = \\frac{n!}{k!(n-k)!} )",
+ INSERT_LATEX: "插入 LaTeX 公式到该绘图",
+ ENTER_LATEX:
+ "输入 LaTeX 表达式(示例: \\binom{n}{k} = \\frac{n!}{k!(n-k)!} )",
//ExcalidrawView.ts
INSTALL_SCRIPT_BUTTON: "安装或更新 Excalidraw 自动化脚本",
OPEN_AS_MD: "打开为 Markdown 文件",
SAVE_AS_PNG: "导出 PNG 到当前目录(按住 CTRL/CMD 指定导出位置)",
SAVE_AS_SVG: "导出 SVG 到当前目录(按住 CTRL/CMD 指定导出位置)",
- OPEN_LINK: "将所选文本作为链接打开 \n(按住 SHIFT 并点击此命令可在新面板打开)",
+ OPEN_LINK:
+ "将所选文本作为链接打开 \n(按住 SHIFT 并点击此命令可在新面板打开)",
EXPORT_EXCALIDRAW: "导出为 .Excalidraw 文件",
LINK_BUTTON_CLICK_NO_TEXT:
"请选择一个含有内部链接或外部链接的图形或文本元素。\n" +
@@ -55,15 +53,12 @@ export default {
"也可以在画布中按住 CTRL/CMD 并点击图形或文本元素!",
TEXT_ELEMENT_EMPTY:
"未选中图形或文本元素,或者元素不包含有效的链接([[链接|缩写]] 或 [缩写](链接))",
- FILENAME_INVALID_CHARS:
- '文件名不能含有以下符号: * " \\ < > : | ?',
+ FILENAME_INVALID_CHARS: '文件名不能含有以下符号: * " \\ < > : | ?',
FILE_DOES_NOT_EXIST:
"文件不存在。按住 ALT(或 ALT + SHIFT)并点击链接来创建新文件。",
- FORCE_SAVE:
- "强制保存并更新相邻面板。\n(注意:自动保存功能始终是开启的)",
+ FORCE_SAVE: "强制保存并更新相邻面板。\n(注意:自动保存功能始终是开启的)",
RAW: "含链接的文本元素正以原文模式显示。\n点击切换到预览模式",
- PARSED:
- "含链接的文本元素正以预览模式显示。\n点击切换到原文模式",
+ PARSED: "含链接的文本元素正以预览模式显示。\n点击切换到原文模式",
NOFILE: "Excalidraw(没有文件)",
COMPATIBILITY_MODE:
"*.excalidraw 文件以兼容模式打开。转换为新格式以获得完整的插件功能。",
@@ -71,8 +66,7 @@ export default {
//settings.ts
FOLDER_NAME: "Excalidraw 文件夹",
- FOLDER_DESC:
- "新绘图的默认位置。如果此处为空,将在库的根目录中创建绘图。",
+ FOLDER_DESC: "新绘图的默认位置。如果此处为空,将在库的根目录中创建绘图。",
TEMPLATE_NAME: "Excalidraw 模板文件",
TEMPLATE_DESC:
"Excalidraw 模板文件的完整路径。" +
@@ -188,15 +182,13 @@ export default {
MD_TRANSCLUDE_HEIGHT_DESC:
"以图像形式嵌入到绘图中的 Markdown 文档产生的图像高度取决于文档内容的多少,但最大不会超过该值。" +
"您可以将绘图打开为 Markdown 文件,用 [[文档名#^块引ID|宽度x最大高度]] 的形式,来单独为该嵌入的文档设定此项。",
- MD_DEFAULT_FONT_NAME:
- "以图像形式嵌入到绘图中的 Markdown 文档的默认字体",
+ MD_DEFAULT_FONT_NAME: "以图像形式嵌入到绘图中的 Markdown 文档的默认字体",
MD_DEFAULT_FONT_DESC:
- '可以设为 Virgil,Casadia 或其他 .ttf/.woff/.woff2 字体文件(如 MyFont.woff2)。' +
+ "可以设为 Virgil,Casadia 或其他 .ttf/.woff/.woff2 字体文件(如 MyFont.woff2)。" +
'您可以在该 Markdown 文档的 Frontmatter 中添加形如 "excalidraw-font: 字体或文件名" 的键值对,来为其单独设定此项。',
- MD_DEFAULT_COLOR_NAME:
- "以图像形式嵌入到绘图中的 Markdown 文档的默认文本颜色",
+ MD_DEFAULT_COLOR_NAME: "以图像形式嵌入到绘图中的 Markdown 文档的默认文本颜色",
MD_DEFAULT_COLOR_DESC:
- '设为 css 颜色名,如 steelblue(参考 https://www.w3schools.com/colors/colors_names.asp),或者有效的 16 进制颜色值,例如 #e67700。' +
+ "设为 css 颜色名,如 steelblue(参考 https://www.w3schools.com/colors/colors_names.asp),或者有效的 16 进制颜色值,例如 #e67700。" +
'您可以在该 Markdown 文档的 Frontmatter 中添加形如 "excalidraw-font-color: 颜色名或颜色值" 的键值对,来为其单独设定此项。',
MD_CSS_NAME: "CSS 文件",
MD_CSS_DESC:
@@ -229,15 +221,13 @@ export default {
EXPORT_PNG_SCALE_NAME: "PNG 导出图像比例",
EXPORT_PNG_SCALE_DESC: "导出的 PNG 图像的大小比例",
EXPORT_BACKGROUND_NAME: "导出的图像包含背景",
- EXPORT_BACKGROUND_DESC:
- "如果关闭,将导出透明背景的图像。",
+ EXPORT_BACKGROUND_DESC: "如果关闭,将导出透明背景的图像。",
EXPORT_THEME_NAME: "导出的图像包含主题",
EXPORT_THEME_DESC:
"导出与绘图的黑暗/明亮主题匹配的图像。" +
"如果关闭,在黑暗主题下导出的图像将和明亮主题一样。",
EXPORT_HEAD: "导出设置",
- EXPORT_SYNC_NAME:
- "保持 .SVG 和 .PNG 文件名与绘图文件同步",
+ EXPORT_SYNC_NAME: "保持 .SVG 和 .PNG 文件名与绘图文件同步",
EXPORT_SYNC_DESC:
"打开后,当绘图文件被重命名时,插件将同步更新同文件夹下的同名 .SVG 和 .PNG 文件。" +
"当绘图文件被删除时,插件将自动删除同文件夹下的同名 .SVG 和 .PNG 文件。",
@@ -252,8 +242,7 @@ export default {
COMPATIBILITY_HEAD: "兼容特性",
EXPORT_EXCALIDRAW_NAME: "自动导出 Excalidraw 文件",
EXPORT_EXCALIDRAW_DESC: "类似于自动导出 SVG,但导出格式为 *.Excalidraw",
- SYNC_EXCALIDRAW_NAME:
- "同步同一绘图的两种格式",
+ SYNC_EXCALIDRAW_NAME: "同步同一绘图的两种格式",
SYNC_EXCALIDRAW_DESC:
"如果 *.excalidraw 格式文件的修改日期比 *.md 格式文件更新," +
"则根据 .excalidraw 文件来更新 .md 文件中的绘图",
@@ -266,24 +255,26 @@ export default {
EXPERIMENTAL_DESC:
"这些设置不会立即生效,需要刷新文件资源管理器或者重新启动 Obsidian 才会生效。",
FILETYPE_NAME: "在文件浏览器中为 Excalidraw 绘图文件添加类型标识符(如 ✏️)",
- FILETYPE_DESC:
- "可通过下一项设置来自定义类型标识符。",
+ FILETYPE_DESC: "可通过下一项设置来自定义类型标识符。",
FILETAG_NAME: "设置 Excalidraw 绘图文件的类型标识符",
FILETAG_DESC: "要显示为类型标识符的 emoji 或文本。",
INSERT_EMOJI: "插入 emoji",
LIVEPREVIEW_NAME: "在实时预览编辑模式中,嵌入到文档中的绘图以图像的方式渲染",
- LIVEPREVIEW_DESC: "开启此项,则可在实时预览编辑模式中,用形如 ![[绘图|宽度|样式]] 的方式来嵌入绘图。" +
- "该选项不会在已打开的文档中立刻生效 —— " +
- "你需要重新打开此文档来使其生效。",
+ LIVEPREVIEW_DESC:
+ "开启此项,则可在实时预览编辑模式中,用形如 ![[绘图|宽度|样式]] 的方式来嵌入绘图。" +
+ "该选项不会在已打开的文档中立刻生效 —— " +
+ "你需要重新打开此文档来使其生效。",
ENABLE_FOURTH_FONT_NAME: "为文本元素启用本地字体",
- ENABLE_FOURTH_FONT_DESC: "开启此项后,文本元素的属性面板里会多出一个本地字体按钮。" +
- "使用了本地字体的绘图文件,将会失去一部分跨平台能力 —— " +
- "若将绘图文件移动到其他库中打开,显示效果可能会截然不同;" +
- "若在 excalidraw.com 或者其他版本的 Excalidraw 中打开,使用本地字体的文本会变回系统默认字体。",
+ ENABLE_FOURTH_FONT_DESC:
+ "开启此项后,文本元素的属性面板里会多出一个本地字体按钮。" +
+ "使用了本地字体的绘图文件,将会失去一部分跨平台能力 —— " +
+ "若将绘图文件移动到其他库中打开,显示效果可能会截然不同;" +
+ "若在 excalidraw.com 或者其他版本的 Excalidraw 中打开,使用本地字体的文本会变回系统默认字体。",
FOURTH_FONT_NAME: "本地字体文件",
- FOURTH_FONT_DESC: "选择库文件夹中的一个 .ttf, .woff 或 .woff2 字体文件作为本地字体文件。" +
- "若未选择文件,则使用默认的 Virgil 字体。",
-
+ FOURTH_FONT_DESC:
+ "选择库文件夹中的一个 .ttf, .woff 或 .woff2 字体文件作为本地字体文件。" +
+ "若未选择文件,则使用默认的 Virgil 字体。",
+
//openDrawings.ts
SELECT_FILE: "选择一个文件后按回车。",
NO_MATCH: "无法匹配到你所查询的文件。",
diff --git a/src/main.ts b/src/main.ts
index 1c72689..f7fa170 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -7,7 +7,6 @@ import {
PluginManifest,
MarkdownView,
normalizePath,
- MarkdownPostProcessorContext,
Menu,
MenuItem,
TAbstractFile,
@@ -42,7 +41,7 @@ import {
VIRGIL_FONT,
VIRGIL_DATAURL,
} from "./constants";
-import ExcalidrawView, { ExportSettings, TextMode } from "./ExcalidrawView";
+import ExcalidrawView, { TextMode } from "./ExcalidrawView";
import { getMarkdownDrawingSection } from "./ExcalidrawData";
import {
ExcalidrawSettings,
@@ -57,8 +56,6 @@ import {
initExcalidrawAutomate,
destroyExcalidrawAutomate,
ExcalidrawAutomate,
- createSVG,
- createPNG,
} from "./ExcalidrawAutomate";
import { Prompt } from "./Prompt";
import { around } from "monkey-around";
@@ -66,23 +63,23 @@ import { t } from "./lang/helpers";
import {
checkAndCreateFolder,
download,
- embedFontsInSVG,
errorlog,
getAttachmentsFolderAndFilePath,
getFontDataURL,
- getIMGFilename,
getIMGPathFromExcalidrawFile,
getNewUniqueFilepath,
isObsidianThemeDark,
log,
- splitFolderAndFilename,
- svgToBase64,
} from "./Utils";
import { OneOffs } from "./OneOffs";
import { FileId } from "@zsviczian/excalidraw/types/element/types";
-import { EmbeddedFilesLoader } from "./EmbeddedFileLoader";
import { ScriptEngine } from "./Scripts";
-import { hoverEvent, initializeMarkdownPostProcessor, markdownPostProcessor, observer } from "./MarkdownPostProcessor";
+import {
+ hoverEvent,
+ initializeMarkdownPostProcessor,
+ markdownPostProcessor,
+ observer,
+} from "./MarkdownPostProcessor";
declare module "obsidian" {
interface App {
@@ -191,10 +188,16 @@ export default class ExcalidrawPlugin extends Plugin {
}
public initializeFourthFont() {
- this.app.workspace.onLayoutReady(async() => {
- const font = (await getFontDataURL(this.app,this.settings.experimantalFourthFont,"","LocalFont"));
- const fourthFontDataURL = font.dataURL === "" ? VIRGIL_DATAURL : font.dataURL;
- this.fourthFontDef = font.fontDef
+ this.app.workspace.onLayoutReady(async () => {
+ const font = await getFontDataURL(
+ this.app,
+ this.settings.experimantalFourthFont,
+ "",
+ "LocalFont",
+ );
+ const fourthFontDataURL =
+ font.dataURL === "" ? VIRGIL_DATAURL : font.dataURL;
+ this.fourthFontDef = font.fontDef;
const newStylesheet = document.createElement("style");
newStylesheet.id = "local-font-stylesheet";
newStylesheet.textContent = `
@@ -210,7 +213,7 @@ export default class ExcalidrawPlugin extends Plugin {
if (oldStylesheet) {
document.head.removeChild(oldStylesheet);
}
-
+
await (document as any).fonts.load(`20px LocalFont`);
});
}
@@ -258,40 +261,45 @@ export default class ExcalidrawPlugin extends Plugin {
}
});
}
-
- private registerInstallCodeblockProcessor() {
- const codeblockProcessor = async (
- source: string,
- el: HTMLElement,
- ctx: MarkdownPostProcessorContext,
- plugin: ExcalidrawPlugin,
- ) => {
+ private registerInstallCodeblockProcessor() {
+ const codeblockProcessor = async (source: string, el: HTMLElement) => {
//Button next to the "List of available scripts" at the top
- //In try/catch block because this approach is very error prone, depends on
+ //In try/catch block because this approach is very error prone, depends on
//MarkdownRenderer() and index.md structure, in case these are not as
//expected this code will break
let button2: HTMLButtonElement = null;
try {
- const link:HTMLElement = el.parentElement.querySelector(`a[href="#${
- el.previousElementSibling.getAttribute("data-heading")}"]`);
+ const link: HTMLElement = el.parentElement.querySelector(
+ `a[href="#${el.previousElementSibling.getAttribute(
+ "data-heading",
+ )}"]`,
+ );
link.style.paddingRight = "10px";
- button2 = link.parentElement.createEl("button",null,(b) => {
+ button2 = link.parentElement.createEl("button", null, (b) => {
b.setText(t("UPDATE_SCRIPT"));
b.addClass("mod-cta");
b.style.backgroundColor = "var(--interactive-success)";
b.style.display = "none";
});
- } catch(e) {
- errorlog({where:"this.registerInstallCodeblockProcessor", source, error:e});
+ } catch (e) {
+ errorlog({
+ where: "this.registerInstallCodeblockProcessor",
+ source,
+ error: e,
+ });
}
source = source.trim();
el.createEl("button", null, async (button) => {
- const setButtonText = (text:"CHECKING" | "INSTALL" | "UPTODATE" | "UPDATE" | "ERROR") => {
- if(button2) button2.style.display = "none";
- switch(text) {
- case "CHECKING":
+ const setButtonText = (
+ text: "CHECKING" | "INSTALL" | "UPTODATE" | "UPDATE" | "ERROR",
+ ) => {
+ if (button2) {
+ button2.style.display = "none";
+ }
+ switch (text) {
+ case "CHECKING":
button.setText(t("CHECKING_SCRIPT"));
button.style.backgroundColor = "var(--interactive-normal)";
break;
@@ -306,75 +314,80 @@ export default class ExcalidrawPlugin extends Plugin {
case "UPDATE":
button.setText(t("UPDATE_SCRIPT"));
button.style.backgroundColor = "var(--interactive-success)";
- if(button2) button2.style.display = null;
+ if (button2) {
+ button2.style.display = null;
+ }
break;
case "ERROR":
button.setText(t("UNABLETOCHECK_SCRIPT"));
button.style.backgroundColor = "var(--interactive-normal)";
break;
}
- }
+ };
button.addClass("mod-cta");
let decodedURI = source;
- try{
+ try {
decodedURI = decodeURI(source);
- } catch(e) {
+ } catch (e) {
errorlog({
- where:"ExcalidrawPlugin.registerInstallCodeblockProcessor.codeblockProcessor.onClick",
+ where:
+ "ExcalidrawPlugin.registerInstallCodeblockProcessor.codeblockProcessor.onClick",
source,
- error:e,
+ error: e,
});
}
- const fname = decodedURI.substring(
- decodedURI.lastIndexOf("/") + 1,
- );
- const folder = `${
- this.settings.scriptFolderPath
- }/${SCRIPT_INSTALL_FOLDER}`;
+ const fname = decodedURI.substring(decodedURI.lastIndexOf("/") + 1);
+ const folder = `${this.settings.scriptFolderPath}/${SCRIPT_INSTALL_FOLDER}`;
const path = `${folder}/${fname}`;
let f = this.app.vault.getAbstractFileByPath(path);
- setButtonText(f?"CHECKING":"INSTALL");
+ setButtonText(f ? "CHECKING" : "INSTALL");
button.onclick = async () => {
try {
- const data = await request({url:source});
- if(f) {
- await this.app.vault.modify(f as TFile,data);
+ const data = await request({ url: source });
+ if (f) {
+ await this.app.vault.modify(f as TFile, data);
} else {
- await checkAndCreateFolder(this.app.vault,folder);
- f = await this.app.vault.create(path,data);
- }
+ await checkAndCreateFolder(this.app.vault, folder);
+ f = await this.app.vault.create(path, data);
+ }
setButtonText("UPTODATE");
- new Notice(`Installed: ${(f as TFile).basename}`)
+ new Notice(`Installed: ${(f as TFile).basename}`);
} catch (e) {
new Notice(`Error installing script: ${fname}`);
errorlog({
- where:"ExcalidrawPlugin.registerInstallCodeblockProcessor.codeblockProcessor.onClick",
- error:e,
+ where:
+ "ExcalidrawPlugin.registerInstallCodeblockProcessor.codeblockProcessor.onClick",
+ error: e,
});
}
};
- if(button2) button2.onclick = button.onclick;
-
+ if (button2) {
+ button2.onclick = button.onclick;
+ }
+
//check modified date on github
//https://superuser.com/questions/1406875/how-to-get-the-latest-commit-date-of-a-file-from-a-given-github-reposotiry
- if(!f || !(f instanceof TFile)) return;
- const msgHead = "https://api.github.com/repos/zsviczian/obsidian-excalidraw-plugin/commits?path=ea-scripts%2F";
+ if (!f || !(f instanceof TFile)) {
+ return;
+ }
+ const msgHead =
+ "https://api.github.com/repos/zsviczian/obsidian-excalidraw-plugin/commits?path=ea-scripts%2F";
const msgTail = "&page=1&per_page=1";
const data = await request({
- url: msgHead+encodeURI(fname)+msgTail,
+ url: msgHead + encodeURI(fname) + msgTail,
});
- if(!data) {
+ if (!data) {
setButtonText("ERROR");
return;
}
const result = JSON.parse(data);
- if(result.length===0 || !result[0]?.commit?.committer?.date) {
+ if (result.length === 0 || !result[0]?.commit?.committer?.date) {
setButtonText("ERROR");
return;
}
//@ts-ignore
- const mtime = (new Date(result[0].commit.committer.date))/1;
- if(mtime > f.stat.mtime) {
+ const mtime = new Date(result[0].commit.committer.date) / 1;
+ if (mtime > f.stat.mtime) {
setButtonText("UPDATE");
return;
}
@@ -384,13 +397,13 @@ export default class ExcalidrawPlugin extends Plugin {
this.registerMarkdownCodeBlockProcessor(
SCRIPT_INSTALL_CODEBLOCK,
- async (source, el, ctx) => {
+ async (source, el) => {
el.addEventListener(RERENDER_EVENT, async (e) => {
e.stopPropagation();
el.empty();
- codeblockProcessor(source, el, ctx, this);
+ codeblockProcessor(source, el);
});
- codeblockProcessor(source, el, ctx, this);
+ codeblockProcessor(source, el);
},
);
}
@@ -673,26 +686,25 @@ export default class ExcalidrawPlugin extends Plugin {
return;
}
const prefix = this.settings.drawingEmbedPrefixWithFilename
- ? `${activeView.file.basename}_`
- : "";
+ ? `${activeView.file.basename}_`
+ : "";
const date = window
- .moment()
- .format(this.settings.drawingFilenameDateTime)
- const extension = this.settings.compatibilityMode
- ? ".excalidraw"
- : ".excalidraw.md";
- const filename = prefix + date + extension;
+ .moment()
+ .format(this.settings.drawingFilenameDateTime);
+ const extension = this.settings.compatibilityMode
+ ? ".excalidraw"
+ : ".excalidraw.md";
+ const filename = prefix + date + extension;
const folder = this.settings.embedUseExcalidrawFolder
- ? null
- : (await getAttachmentsFolderAndFilePath(
- this.app,
- activeView.file.path,
- filename,
- )).folder;
- const file = await this.createDrawing(
- filename,
- folder,
- );
+ ? null
+ : (
+ await getAttachmentsFolderAndFilePath(
+ this.app,
+ activeView.file.path,
+ filename,
+ )
+ ).folder;
+ const file = await this.createDrawing(filename, folder);
await this.embedDrawing(file.path);
this.openDrawing(file, inNewPane);
};
@@ -1133,31 +1145,30 @@ export default class ExcalidrawPlugin extends Plugin {
);
}
- public ctrlKeyDown:boolean;
- public shiftKeyDown:boolean;
- public altKeyDown:boolean;
- public onKeyUp:any;
- public onKeyDown:any;
+ public ctrlKeyDown: boolean;
+ public shiftKeyDown: boolean;
+ public altKeyDown: boolean;
+ public onKeyUp: any;
+ public onKeyDown: any;
private popScope: Function = null;
private registerEventListeners() {
const self = this;
this.app.workspace.onLayoutReady(async () => {
-
- self.onKeyUp = (e:KeyboardEvent) => {
- self.ctrlKeyDown = e[CTRL_OR_CMD];
+ self.onKeyUp = (e: KeyboardEvent) => {
+ self.ctrlKeyDown = e[CTRL_OR_CMD];
self.shiftKeyDown = e.shiftKey;
self.altKeyDown = e.altKey;
- }
+ };
- self.onKeyDown = (e:KeyboardEvent) => {
- this.ctrlKeyDown = e[CTRL_OR_CMD];
+ self.onKeyDown = (e: KeyboardEvent) => {
+ this.ctrlKeyDown = e[CTRL_OR_CMD];
this.shiftKeyDown = e.shiftKey;
this.altKeyDown = e.altKey;
- }
+ };
- window.addEventListener("keydown",self.onKeyDown,false);
- window.addEventListener("keyup",self.onKeyUp,false);
+ window.addEventListener("keydown", self.onKeyDown, false);
+ window.addEventListener("keyup", self.onKeyUp, false);
//watch filename change to rename .svg, .png; to sync to .md; to update links
const renameEventHandler = async (
@@ -1331,8 +1342,8 @@ export default class ExcalidrawPlugin extends Plugin {
}
onunload() {
- window.removeEventListener("keydown",this.onKeyDown,false);
- window.removeEventListener("keyup",this.onKeyUp,false);
+ window.removeEventListener("keydown", this.onKeyDown, false);
+ window.removeEventListener("keyup", this.onKeyUp, false);
destroyExcalidrawAutomate();
if (this.popScope) {
diff --git a/src/settings.ts b/src/settings.ts
index 341bb23..a1abaa6 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -146,7 +146,9 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}
async hide() {
- this.plugin.settings.scriptFolderPath = normalizePath(this.plugin.settings.scriptFolderPath);
+ this.plugin.settings.scriptFolderPath = normalizePath(
+ this.plugin.settings.scriptFolderPath,
+ );
if (
this.plugin.settings.scriptFolderPath === "/" ||
this.plugin.settings.scriptFolderPath === ""
@@ -570,24 +572,23 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
);
new Setting(containerEl)
- .setName(t("MD_DEFAULT_FONT_NAME"))
- .setDesc(t("MD_DEFAULT_FONT_DESC"))
+ .setName(t("MD_DEFAULT_FONT_NAME"))
+ .setDesc(t("MD_DEFAULT_FONT_DESC"))
.addDropdown(async (d: DropdownComponent) => {
d.addOption("Virgil", "Virgil");
d.addOption("Cascadia", "Cascadia");
- this.app.vault.getFiles()
- .filter((f)=>["ttf", "woff", "woff2"].contains(f.extension))
- .forEach((f:TFile)=>{
- d.addOption(f.path,f.name)
- })
- d.setValue(this.plugin.settings.mdFont)
- .onChange((value) => {
- this.requestReloadDrawings = true;
- this.plugin.settings.mdFont = value;
- this.applySettingsUpdate(true);
+ this.app.vault
+ .getFiles()
+ .filter((f) => ["ttf", "woff", "woff2"].contains(f.extension))
+ .forEach((f: TFile) => {
+ d.addOption(f.path, f.name);
});
+ d.setValue(this.plugin.settings.mdFont).onChange((value) => {
+ this.requestReloadDrawings = true;
+ this.plugin.settings.mdFont = value;
+ this.applySettingsUpdate(true);
+ });
});
-
new Setting(containerEl)
.setName(t("MD_DEFAULT_COLOR_NAME"))
@@ -878,7 +879,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}),
);
- new Setting(containerEl)
+ new Setting(containerEl)
.setName(t("ENABLE_FOURTH_FONT_NAME"))
.setDesc(t("ENABLE_FOURTH_FONT_DESC"))
.addToggle((toggle) =>
@@ -891,24 +892,200 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}),
);
- new Setting(containerEl)
+ new Setting(containerEl)
.setName(t("FOURTH_FONT_NAME"))
.setDesc(t("FOURTH_FONT_DESC"))
- .addDropdown(async (d: DropdownComponent) => {
- d.addOption("Virgil", "Virgil");
- this.app.vault.getFiles()
- .filter((f)=>["ttf", "woff", "woff2"].contains(f.extension))
- .forEach((f:TFile)=>{
- d.addOption(f.path,f.name)
- })
- d.setValue(this.plugin.settings.experimantalFourthFont)
- .onChange((value) => {
- this.requestReloadDrawings = true;
- this.plugin.settings.experimantalFourthFont = value;
- this.applySettingsUpdate(true);
- this.plugin.initializeFourthFont();
- });
- });
+ .addDropdown(async (d: DropdownComponent) => {
+ d.addOption("Virgil", "Virgil");
+ this.app.vault
+ .getFiles()
+ .filter((f) => ["ttf", "woff", "woff2"].contains(f.extension))
+ .forEach((f: TFile) => {
+ d.addOption(f.path, f.name);
+ });
+ d.setValue(this.plugin.settings.experimantalFourthFont).onChange(
+ (value) => {
+ this.requestReloadDrawings = true;
+ this.plugin.settings.experimantalFourthFont = value;
+ this.applySettingsUpdate(true);
+ this.plugin.initializeFourthFont();
+ },
+ );
+ });
+ if (Object.keys(this.plugin.settings.scriptEngineSettings).length > 0) {
+ const getValue = (scriptName: string, variableName: string): any => {
+ const variable =
+ //@ts-ignore
+ this.plugin.settings.scriptEngineSettings[scriptName][variableName];
+ switch (typeof variable) {
+ case "object":
+ return variable.value;
+ default:
+ return variable;
+ }
+ };
+
+ const setValue = (
+ scriptName: string,
+ variableName: string,
+ value: any,
+ ) => {
+ switch (
+ //@ts-ignore
+ typeof this.plugin.settings.scriptEngineSettings[scriptName][
+ variableName
+ ]
+ ) {
+ case "object":
+ //@ts-ignore
+ this.plugin.settings.scriptEngineSettings[scriptName][
+ variableName
+ ].value = value;
+ break;
+ default:
+ //@ts-ignore
+ this.plugin.settings.scriptEngineSettings[scriptName][
+ variableName
+ ] = value;
+ }
+ };
+
+ const addBooleanSetting = (
+ scriptName: string,
+ variableName: string,
+ description?: string,
+ ) => {
+ new Setting(containerEl)
+ .setName(variableName)
+ .setDesc(description ?? "")
+ .addToggle((toggle) =>
+ toggle
+ .setValue(getValue(scriptName, variableName))
+ .onChange(async (value) => {
+ setValue(scriptName, variableName, value);
+ this.applySettingsUpdate();
+ }),
+ );
+ };
+
+ const addStringSetting = (
+ scriptName: string,
+ variableName: string,
+ description?: string,
+ valueset?: any,
+ ) => {
+ if (
+ valueset &&
+ Object.prototype.toString.call(valueset) === "[object Array]" &&
+ valueset.length > 0
+ ) {
+ new Setting(containerEl)
+ .setName(variableName)
+ .setDesc(description ?? "")
+ .addDropdown((dropdown) => {
+ valueset.forEach((val: any) =>
+ dropdown.addOption(val.toString(), val.toString()),
+ );
+ dropdown
+ .setValue(getValue(scriptName, variableName))
+ .onChange(async (value) => {
+ setValue(scriptName, variableName, value);
+ this.applySettingsUpdate();
+ });
+ });
+ } else {
+ new Setting(containerEl)
+ .setName(variableName)
+ .setDesc(description ?? "")
+ .addText((text) =>
+ text
+ .setValue(getValue(scriptName, variableName))
+ .onChange(async (value) => {
+ setValue(scriptName, variableName, value);
+ this.applySettingsUpdate();
+ }),
+ );
+ }
+ };
+
+ const addNumberSetting = (
+ scriptName: string,
+ variableName: string,
+ description?: string,
+ ) => {
+ new Setting(containerEl)
+ .setName(variableName)
+ .setDesc(description ?? "")
+ .addText((text) =>
+ text
+ .setPlaceholder("Enter a number")
+ .setValue(getValue(scriptName, variableName).toString())
+ .onChange(async (value) => {
+ const numVal = parseFloat(value);
+ if (isNaN(numVal) && value !== "") {
+ text.setValue(getValue(scriptName, variableName).toString());
+ return;
+ }
+ setValue(scriptName, variableName, isNaN(numVal) ? 0 : numVal);
+ this.applySettingsUpdate();
+ }),
+ );
+ };
+
+ this.containerEl.createEl("h1", { text: t("SCRIPT_SETTINGS_HEAD") });
+ Object.keys(this.plugin.settings.scriptEngineSettings).forEach(
+ (scriptName: string) => {
+ const settings =
+ //@ts-ignore
+ this.plugin.settings.scriptEngineSettings[scriptName];
+ const values = Object.values(settings);
+ if (
+ values.length === 0 ||
+ (values.length > 0 &&
+ values
+ .map((val: any): number => (val.hidden ? 0 : 1))
+ .reduce((prev, cur) => prev + cur) === 0)
+ ) {
+ return;
+ }
+ this.containerEl.createEl("h3", { text: scriptName });
+ Object.keys(settings).forEach((variableName) => {
+ const variable = settings[variableName];
+ const item = variable.value ?? variable;
+ switch (typeof item) {
+ case "boolean":
+ if (!variable.hidden) {
+ addBooleanSetting(
+ scriptName,
+ variableName,
+ variable.description,
+ );
+ }
+ break;
+ case "string":
+ if (!variable.hidden) {
+ addStringSetting(
+ scriptName,
+ variableName,
+ variable.description,
+ variable.valueset,
+ );
+ }
+ break;
+ case "number":
+ if (!variable.hidden) {
+ addNumberSetting(
+ scriptName,
+ variableName,
+ variable.description,
+ );
+ }
+ break;
+ }
+ });
+ },
+ );
+ }
}
}
diff --git a/versions.json b/versions.json
index cfc6f83..f7625da 100644
--- a/versions.json
+++ b/versions.json
@@ -1,4 +1,4 @@
{
- "1.5.20": "0.12.16",
+ "1.5.21": "0.12.16",
"1.4.2": "0.11.13"
}