Compare commits

..

46 Commits

Author SHA1 Message Date
Zsolt Viczian
0a2ef2d751 1.5.30 2022-02-03 07:40:24 +01:00
Zsolt Viczian
1d830526a7 1.5.29 2022-02-02 21:59:24 +01:00
Zsolt Viczian
71f1072822 1.5.28 2022-02-01 23:22:29 +01:00
Zsolt Viczian
8029c5d4cd 1.5.27 2022-02-01 22:26:04 +01:00
Zsolt Viczian
962c6a71f7 improved new link handling 2022-02-01 21:56:19 +01:00
zsviczian
3e0a6e839f Update README.md 2022-01-30 16:11:10 +01:00
Zsolt Viczian
7aa416766c updated script description 2022-01-30 13:47:32 +01:00
Zsolt Viczian
3dae930201 1.5.26 2022-01-30 10:10:34 +01:00
Zsolt Viczian
379c2d0e52 1.5.25 2022-01-29 16:54:37 +01:00
Zsolt Viczian
d9a1113f25 changed filename 2022-01-29 15:38:21 +01:00
Zsolt Viczian
d0d5677c81 select all if only one type of element in view 2022-01-29 14:52:01 +01:00
Zsolt Viczian
a364c0fe45 typo 2022-01-29 14:37:41 +01:00
Zsolt Viczian
bf9a917af3 typo 2022-01-29 14:29:34 +01:00
Zsolt Viczian
7d98bf691c 1.5.24 2022-01-29 14:20:34 +01:00
Zsolt Viczian
b927a48eb3 add image 2022-01-29 14:03:15 +01:00
Zsolt Viczian
7f2af801a9 add image 2022-01-29 10:23:06 +01:00
Zsolt Viczian
2c2bbc2d62 add image 2022-01-29 10:21:08 +01:00
Zsolt Viczian
f66cf344da upload image 2022-01-29 08:10:11 +01:00
Zsolt Viczian
6c4169e9c9 . 2022-01-28 19:38:41 +01:00
Zsolt Viczian
92f8b68445 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2022-01-28 19:35:50 +01:00
Zsolt Viczian
d0bfd834c8 1.5.23 2022-01-28 19:33:32 +01:00
zsviczian
b00737c40a Merge pull request #400 from 1-2-3/master
feat: add Fixed inner distance script
2022-01-28 08:38:45 +01:00
Zsolt Viczian
59a8fbf909 fieldsuggestor toLowerCase 2022-01-28 08:37:47 +01:00
zahuifan
95e3bb0ddf feat: add Fixed inner distance script 2022-01-28 15:06:45 +08:00
Zsolt Viczian
7107b478b4 ExcalidrawAutomate field suggestor 2022-01-28 06:56:38 +01:00
zsviczian
cb43187738 Update index.md 2022-01-27 15:02:35 +01:00
zsviczian
de8a921c04 Update Add Link and Open Page.md 2022-01-27 15:01:57 +01:00
zsviczian
ef8f3497d2 Update Add Link and Open Page.md 2022-01-27 15:00:55 +01:00
zsviczian
90c66c411c Merge pull request #398 from 1-2-3/master
feat: add fixed center distance scripts (#394)
2022-01-27 14:21:26 +01:00
zahuifan
bc8a2cb912 feat: add fixed center distance scripts (#394) 2022-01-27 20:29:09 +08:00
zsviczian
0429a76b39 Update ExcalidrawAutomateFieldSuggestor.ts 2022-01-27 11:09:54 +01:00
Zsolt Viczian
46cbcc581c 1.5.23 WIP (ExcalidrawAutomate suggester) 2022-01-26 21:57:27 +01:00
Zsolt Viczian
4fd5c13d1e 1.5.22 2022-01-25 22:08:42 +01:00
zsviczian
a285e1aeee Update OCR - Optical Character Recognition.md 2022-01-25 15:08:57 +01:00
zsviczian
d342dae47d Merge pull request #389 from 1-2-3/master
feat: add settings to scripts
2022-01-25 12:52:30 +01:00
zahuifan
ab1e38da81 feat: add settings to scripts 2022-01-25 15:31:33 +08:00
zsviczian
c71a5b2403 Update Add Next Step in Process.md 2022-01-25 07:48:26 +01:00
zsviczian
f8126709cc Merge pull request #386 from 1-2-3/master
feat: add icons to shape list
2022-01-24 10:49:35 +01:00
zahuifan
69abe47e9b feat: add icons to shape list 2022-01-24 14:49:23 +08:00
zsviczian
9ea915339e Merge pull request #385 from 1-2-3/master
fix: link in ea-scripts/readme.md
2022-01-24 07:33:17 +01:00
zahuifan
b44125773a fix: link in ea-scripts/readme.md 2022-01-24 12:48:06 +08:00
Zsolt Viczian
208284405b add link, add to top layer 2022-01-23 21:02:26 +01:00
Zsolt Viczian
2193bcf5ce minor change for demo in the video 2022-01-23 19:17:06 +01:00
Zsolt Viczian
f35a5bc948 updated script 2022-01-23 18:55:40 +01:00
Zsolt Viczian
7a69fb3570 updated script 2022-01-23 18:03:12 +01:00
Zsolt Viczian
10a710127a updates scripts 2022-01-23 17:41:18 +01:00
48 changed files with 2535 additions and 452 deletions

View File

@@ -13,7 +13,7 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
|[![9 Excalidraw Automate](https://user-images.githubusercontent.com/14358394/125160367-bdb6cc00-e17c-11eb-92f1-6f59faea85fd.jpg)](https://youtu.be/VRZVujfVab0)|[![10 Miscellaneous](https://user-images.githubusercontent.com/14358394/125160374-c3141680-e17c-11eb-8cc2-dfaffd903d15.jpg)](https://youtu.be/D1iBYo1_jjc)|[![Image Elements](https://user-images.githubusercontent.com/14358394/138607067-ccb62f92-48a4-4880-ac6e-68c1bf86ac2c.png)](https://www.youtube.com/watch?v=_c_0zpBJ4Xc&)|
|[![LaTex Demo](https://user-images.githubusercontent.com/14358394/143732412-1c65227e-4381-406d-847a-b001ab3506ca.jpg)](https://youtu.be/r08wk-58DPk)|[![markdown embeds](https://user-images.githubusercontent.com/14358394/143732440-90bfa029-8615-462e-ada3-c903d71a82c9.jpg)](https://youtu.be/tsecSfnTMow)|[![markdownAdvanced](https://user-images.githubusercontent.com/14358394/143783906-15cee494-c6d5-4495-a2ca-74634e4e7355.jpg)](https://youtu.be/K6qZkTz8GHs)|
|[![Script Engine](https://user-images.githubusercontent.com/14358394/145684531-8d9c2992-59ac-4ebc-804a-4cce1777ded2.jpg)](https://youtu.be/hePJcObHIso)|[![sticky notes thumbnail](https://user-images.githubusercontent.com/14358394/147283367-e5689385-ea51-4983-81a3-04d810d39f62.jpg)](https://youtu.be/NOuddK6xrr8)|[![plugin store](https://user-images.githubusercontent.com/14358394/147889174-6c306d0d-2d29-46cc-a53f-3f0013cf14de.jpg)](https://youtu.be/lzYdOQ6z8F0)|
|[![fourtfont](https://user-images.githubusercontent.com/14358394/149659524-2a4e0a24-40c9-4e66-a6b1-c92f3b88ecd5.jpg)](https://youtu.be/eKFmrSQhFA4)|||
|[![fourtfont](https://user-images.githubusercontent.com/14358394/149659524-2a4e0a24-40c9-4e66-a6b1-c92f3b88ecd5.jpg)](https://youtu.be/eKFmrSQhFA4)|[![thumbnail](https://user-images.githubusercontent.com/14358394/151705333-54e9ffd2-0bd7-4d02-b99e-0bd4e4708d4d.jpg)](https://youtu.be/qbPIAZguJeo)||
# Key features

View File

@@ -134,7 +134,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
@@ -185,13 +185,16 @@ 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<void>; //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<void>; //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:
//if(!(ea.verifyMinimumPluginVersion && ea.verifyMinimumPluginVersion("1.5.20"))) {new Notice("message");return;}
verifyMinimumPluginVersion(requiredVersion: string):boolean;
//recommended use:
//if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.20")) {new Notice("message");return;}
verifyMinimumPluginVersion(requiredVersion: string): boolean;
selectElementsInView(elements: ExcalidrawElement[]): void; //sets selection in view
generateElementId(): string; //returns an 8 character long random id
cloneElement(element: ExcalidrawElement): ExcalidrawElement; //Returns a clone of the element with a new id
}
```

View File

@@ -1,7 +1,7 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-link-and-open.jpg)
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.
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. Creates empty markdown file by default, this can be changed to creating a drawing by default via settings.
```javascript
*/
@@ -11,23 +11,38 @@ if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
return;
}
const BLANK_DRAWING = ["---","","excalidraw-plugin: parsed","","---","==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==","","","%%","# Drawing","\x60\x60\x60json",'{"type":"excalidraw","version":2,"source":"https://excalidraw.com","elements":[],"appState":{"gridSize":null,"viewBackgroundColor":"#ffffff"}}',"\x60\x60\x60","%%"].join("\n");
settings = ea.getScriptSettings();
//set default values on first run
if(!settings["Link position"]) {
settings = {
"Link position" : {
value: "below",
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);
description: "Add link below or above the selected object?"
},
"Link font size" : {
value: 12
}
};
ea.setScriptSettings(settings);
}
if(!settings["New document should be an Excalidraw drawing"]) {
settings = {
"New document should be an Excalidraw drawing": {
value: false,
description: "When adding a new document, should the new document be a blank markdown document (toggle == off) or a blank Excalidraw drawing (toggle=on)?"
},
...settings
};
ea.setScriptSettings(settings);
}
const below = settings["Link position"].value === "below";
const newDocExcalidraw = settings["New document should be an Excalidraw drawing"].value;
const fontSize = Math.floor(settings["Link font size"].value);
elements = ea.getViewSelectedElements();
@@ -46,7 +61,8 @@ if(file) {
} 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`,"");
file = await app.vault.create(`${prefix} ${timestamp}.md`,newDocExcalidraw?BLANK_DRAWING:"");
if(newDocExcalidraw) await new Promise(r => setTimeout(r, 100)); //wait for metadata cache to update, so file opens as excalidraw
}
const filepath = app.metadataCache.fileToLinktext(file,ea.targetView.file.path,true);
@@ -68,5 +84,5 @@ const id = ea.addText(
);
ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup(elements.map((e)=>e.id).concat([id]));
ea.addElementsToView(false);
ea.openFileInNewOrAdjacentLeaf(file);
ea.addElementsToView(false,true,true);
ea.openFileInNewOrAdjacentLeaf(file);

View File

@@ -6,7 +6,7 @@ This script will prompt you for the title of the process step, then will create
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.24")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
@@ -43,6 +43,11 @@ if(!settings["Starting arrowhead"]) {
const arrowStart = settings["Starting arrowhead"].value === "none" ? null : settings["Starting arrowhead"].value;
const arrowEnd = settings["Ending arrowhead"].value === "none" ? null : settings["Ending arrowhead"].value;
// workaround until https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/388 is fixed
if (!arrowEnd) ea.style.endArrowHead = null;
if (!arrowStart) ea.style.startArrowHead = null;
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);
@@ -54,7 +59,8 @@ const elements = ea.getViewSelectedElements();
const isFirst = (!elements || elements.length === 0);
const width = ea.measureText("w".repeat(wrapLineLen)).width;
console.log(width,fixWidth);
let id = "";
if(!isFirst) {
const fromElement = ea.getLargestElement(elements);
@@ -66,22 +72,23 @@ if(!isFirst) {
ea.style.strokeColor = el.strokeColor;
ea.style.fontSize = el.fontSize;
ea.style.fontFamily = el.fontFamily;
ea.style.strokeWidth = el.strokeWidth;
ea.style.strokeStyle = el.strokeStyle;
ea.style.strokeSharpness = el.strokeSharpness;
}
textWidth = ea.measureText(text).width;
const id = ea.addText(
fromElement.x,
id = ea.addText(
fixWidth
? fromElement.x+fromElement.width/2-width/2
: fromElement.x+fromElement.width/2-textWidth/2-textPadding,
fromElement.y+fromElement.height+gapBetweenElements,
text,
{
wrapAt: wrapLineLen,
textAlign: "center",
box: "rectangle",
boxPadding: textPadding,
...fixWidth?{width: width}:null
...fixWidth
? {width: width, boxPadding:0}
: {boxPadding: textPadding}
}
);
@@ -96,9 +103,19 @@ if(!isFirst) {
numberOfPoints: linePoints
}
);
ea.addElementsToView(false);
const rect = ea.getElement(id);
rect.strokeColor = fromElement.strokeColor;
rect.strokeWidth = fromElement.strokeWidth;
rect.strokeStyle = fromElement.strokeStyle;
rect.roughness = fromElement.roughness;
rect.strokeSharpness = fromElement.strokeSharpness;
rect.backgroundColor = fromElement.backgroundColor;
rect.fillStyle = fromElement.fillStyle;
await ea.addElementsToView(false);
} else {
ea.addText(
id = ea.addText(
0,
0,
text,
@@ -110,5 +127,7 @@ if(!isFirst) {
...fixWidth?{width: width}:null
}
);
ea.addElementsToView(true);
await ea.addElementsToView(true);
}
ea.selectElementsInView([ea.getElement(id)]);

View File

@@ -1,152 +1,181 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-each-selected-groups.png)
This script will add encapsulating boxes around each of the currently selected groups in Excalidraw.
You can focus on content creation first, and then batch add consistent style boxes to each group of text.
Tips 1: You can copy the desired style to the global state using script `Copy Selected Element Style to Global`, then add boxes with the same global style using script `Box Each Selected Groups`.
Tips 2: Next you can use scripts `Expand rectangles horizontally keep text centered` and `Expand rectangles vertically keep text centered` to make the boxes the same size, if you wish.
Tips 3: If you want the left and right margins to be different from the top and bottom margins, input something like `32,16`, this will create a box with left and right margins of `32` and top and bottom margins of `16`.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const paddingStr = await utils.inputPrompt("padding?","string","8");
var paddingLR = 0;
var paddingTB = 0;
if(paddingStr.indexOf(',') > 0) {
const paddingParts = paddingStr.split(',');
paddingLR = parseInt(paddingParts[0]);
paddingTB = parseInt(paddingParts[1]);
}
else {
paddingLR = paddingTB = parseInt(paddingStr);
}
if(isNaN(paddingLR) || isNaN(paddingTB)) {
return;
}
const selectedElements = ea.getViewSelectedElements();
const groups = ea.getMaximumGroups(selectedElements);
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
for(const elements of groups) {
if(elements.length === 1 && elements[0].type ==="arrow" || elements[0].type==="line") {
// individual arrows or lines are not affected
continue;
}
const box = ea.getBoundingBox(elements);
color = ea
.getExcalidrawAPI()
.getAppState()
.currentItemStrokeColor;
// use current stroke with and style
const appState = ea.getExcalidrawAPI().getAppState();
const strokeWidth = appState.currentItemStrokeWidth;
const strokeStyle = appState.currentItemStrokeStyle;
const strokeSharpness = appState.currentItemStrokeSharpness;
const roughness = appState.currentItemRoughness;
const fillStyle = appState.currentItemFillStyle;
const backgroundColor = appState.currentItemBackgroundColor;
ea.style.strokeWidth = strokeWidth;
ea.style.strokeStyle = strokeStyle;
ea.style.strokeSharpness = strokeSharpness;
ea.style.roughness = roughness;
ea.style.fillStyle = fillStyle;
ea.style.backgroundColor = backgroundColor;
ea.style.strokeColor = color;
const id = ea.addRect(
box.topX - paddingLR,
box.topY - paddingTB,
box.width + 2*paddingLR,
box.height + 2*paddingTB
);
// Change the join point in the group to the new box
const elementsWithBounded = elements.filter(el => (el.boundElements || []).length > 0);
const boundedElementsCollection = elementsWithBounded.reduce((result, el) => [...result, ...el.boundElements], []);
for(const el of elementsWithBounded) {
el.boundElements = [];
}
const newRect = ea.getElement(id);
newRect.boundElements = boundedElementsCollection;
const elementIds = elements.map(el => el.id);
const startBindingLines = allIndividualArrows.filter(el => elementIds.includes((el.startBinding||{}).elementId));
for(startBindingLine of startBindingLines) {
startBindingLine.startBinding.elementId = id;
recalculateStartPointOfLine(startBindingLine, newRect);
}
const endBindingLines = allIndividualArrows.filter(el => elementIds.includes((el.endBinding||{}).elementId));
for(endBindingLine of endBindingLines) {
endBindingLine.endBinding.elementId = id;
recalculateEndPointOfLine(endBindingLine, newRect);
}
ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup([id].concat(elements.map((el)=>el.id)));
ea.addElementsToView(false);
ea.reset();
}
function recalculateStartPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[1][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[1][1];
line.startBinding.gap = 8;
line.startBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.startBinding.gap
);
if(intersectA.length > 0) {
line.points[0] = [0, 0];
for(var i = 1; i<line.points.length; i++) {
line.points[i][0] -= intersectA[0][0] - line.x;
line.points[i][1] -= intersectA[0][1] - line.y;
}
line.x = intersectA[0][0];
line.y = intersectA[0][1];
}
}
function recalculateEndPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[line.points.length-2][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[line.points.length-2][1];
line.endBinding.gap = 8;
line.endBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.endBinding.gap
);
if(intersectA.length > 0) {
line.points[line.points.length - 1] = [intersectA[0][0] - line.x, intersectA[0][1] - line.y];
}
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-each-selected-groups.png)
This script will add encapsulating boxes around each of the currently selected groups in Excalidraw.
You can focus on content creation first, and then batch add consistent style boxes to each group of text.
Tips 1: You can copy the desired style to the global state using script `Copy Selected Element Style to Global`, then add boxes with the same global style using script `Box Each Selected Groups`.
Tips 2: Next you can use scripts `Expand rectangles horizontally keep text centered` and `Expand rectangles vertically keep text centered` to make the boxes the same size, if you wish.
Tips 3: If you want the left and right margins to be different from the top and bottom margins, input something like `32,16`, this will create a box with left and right margins of `32` and top and bottom margins of `16`.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```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["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"
},
"Remember last padding?": false
};
ea.setScriptSettings(settings);
}
let paddingStr = settings["Default padding"].value.toString();
const rememberLastPadding = settings["Remember last padding?"];
if(settings["Prompt for padding?"]) {
paddingStr = await utils.inputPrompt("padding?","string",paddingStr);
}
if(!paddingStr) {
return;
}
if(rememberLastPadding) {
settings["Default padding"].value = paddingStr;
ea.setScriptSettings(settings);
}
var paddingLR = 0;
var paddingTB = 0;
if(paddingStr.indexOf(',') > 0) {
const paddingParts = paddingStr.split(',');
paddingLR = parseInt(paddingParts[0]);
paddingTB = parseInt(paddingParts[1]);
}
else {
paddingLR = paddingTB = parseInt(paddingStr);
}
if(isNaN(paddingLR) || isNaN(paddingTB)) {
return;
}
const selectedElements = ea.getViewSelectedElements();
const groups = ea.getMaximumGroups(selectedElements);
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
for(const elements of groups) {
if(elements.length === 1 && elements[0].type ==="arrow" || elements[0].type==="line") {
// individual arrows or lines are not affected
continue;
}
const box = ea.getBoundingBox(elements);
color = ea
.getExcalidrawAPI()
.getAppState()
.currentItemStrokeColor;
// use current stroke with and style
const appState = ea.getExcalidrawAPI().getAppState();
const strokeWidth = appState.currentItemStrokeWidth;
const strokeStyle = appState.currentItemStrokeStyle;
const strokeSharpness = appState.currentItemStrokeSharpness;
const roughness = appState.currentItemRoughness;
const fillStyle = appState.currentItemFillStyle;
const backgroundColor = appState.currentItemBackgroundColor;
ea.style.strokeWidth = strokeWidth;
ea.style.strokeStyle = strokeStyle;
ea.style.strokeSharpness = strokeSharpness;
ea.style.roughness = roughness;
ea.style.fillStyle = fillStyle;
ea.style.backgroundColor = backgroundColor;
ea.style.strokeColor = color;
const id = ea.addRect(
box.topX - paddingLR,
box.topY - paddingTB,
box.width + 2*paddingLR,
box.height + 2*paddingTB
);
// Change the join point in the group to the new box
const elementsWithBounded = elements.filter(el => (el.boundElements || []).length > 0);
const boundedElementsCollection = elementsWithBounded.reduce((result, el) => [...result, ...el.boundElements], []);
for(const el of elementsWithBounded) {
el.boundElements = [];
}
const newRect = ea.getElement(id);
newRect.boundElements = boundedElementsCollection;
const elementIds = elements.map(el => el.id);
const startBindingLines = allIndividualArrows.filter(el => elementIds.includes((el.startBinding||{}).elementId));
for(startBindingLine of startBindingLines) {
startBindingLine.startBinding.elementId = id;
recalculateStartPointOfLine(startBindingLine, newRect);
}
const endBindingLines = allIndividualArrows.filter(el => elementIds.includes((el.endBinding||{}).elementId));
for(endBindingLine of endBindingLines) {
endBindingLine.endBinding.elementId = id;
recalculateEndPointOfLine(endBindingLine, newRect);
}
ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup([id].concat(elements.map((el)=>el.id)));
ea.addElementsToView(false);
ea.reset();
}
function recalculateStartPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[1][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[1][1];
line.startBinding.gap = 8;
line.startBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.startBinding.gap
);
if(intersectA.length > 0) {
line.points[0] = [0, 0];
for(var i = 1; i<line.points.length; i++) {
line.points[i][0] -= intersectA[0][0] - line.x;
line.points[i][1] -= intersectA[0][1] - line.y;
}
line.x = intersectA[0][0];
line.y = intersectA[0][1];
}
}
function recalculateEndPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[line.points.length-2][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[line.points.length-2][1];
line.endBinding.gap = 8;
line.endBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.endBinding.gap
);
if(intersectA.length > 0) {
line.points[line.points.length - 1] = [intersectA[0][0] - line.x, intersectA[0][1] - line.y];
}
}

View File

@@ -5,9 +5,10 @@ The script allows you to change the shape of selected Rectangles, Diamonds and E
```javascript
*/
const shapesDispaly=["○ ellipse","□ rectangle","◇ diamond"];
const shapes=["ellipse","rectangle","diamond"];
elements = ea.getViewSelectedElements().filter(el=>shapes.contains(el.type));
newShape = await utils.suggester(shapes, shapes);
newShape = await utils.suggester(shapesDispaly, shapes);
if(!newShape) return;
elements.forEach(el=>el.type = newShape);

View File

@@ -78,4 +78,4 @@ ea.connectObjects(
numberOfPoints: linePoints
}
);
ea.addElementsToView();
ea.addElementsToView(false,true,true);

View File

@@ -0,0 +1,64 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-convert-freedraw-to-line.jpg)
Convert selected freedraw objects into editable lines. This will allow you to adjust your drawings by dragging line points and will also allow you to select shape fill in case of enclosed lines. You can adjust conversion point density in settings.
```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["Point density"]) {
settings = {
"Point density" : {
value: "7:1",
valueset: ["1:1","2:1","3:1","4:1","5:1","6:1","7:1","8:1","9:1","10:1","11:1"],
description: "A freedraw object has many points. Converting freedraw to a line with too many points will result in an impractical object that is hard to edit. This setting sepcifies how many points from freedraw should be averaged to form a point on the line"
},
};
ea.setScriptSettings(settings);
}
const scale = settings["Point density"].value;
const setSize = parseInt(scale.substring(0,scale.indexOf(":")));
const elements = ea.getViewSelectedElements().filter(el=>el.type==="freedraw");
if(elements.length === 0) {
new Notice("No freedraw object is selected");
}
ea.style.roughness=0;
ea.style.strokeSharpness="round";
elements.forEach((el)=>{
points = [];
points.push(el.points[0]);
for(i=1;i<el.points.length;i+=setSize) {
point = [0,0];
count = 0;
for(p of el.points.slice(i,i+setSize)) {
point = [ point[0]+p[0] , point[1]+p[1] ];
count++;
}
point = [point[0]/count,point[1]/count];
points.push(point);
}
const lineId = ea.addLine(points);
const line = ea.getElement(lineId);
line.strokeWidth = el.strokeWidth*3;
line.strokeColor = el.strokeColor;
line.width = el.width;
line.height = el.height;
line.x = el.x;
line.y = el.y;
});
ea.deleteViewElements(elements);
await ea.addElementsToView(false,true,true);
ea.selectElementsInView(ea.getElements());

View File

@@ -0,0 +1,72 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-horizontal-distance-between-centers.png)
This script arranges the selected elements horizontally with a fixed center spacing.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```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["Default distance"]) {
settings = {
"Prompt for distance?": true,
"Default distance" : {
value: 10,
description: "Fixed horizontal distance between centers"
},
"Remember last distance?": false
};
ea.setScriptSettings(settings);
}
let distanceStr = settings["Default distance"].value.toString();
const rememberLastDistance = settings["Remember last distance?"];
if(settings["Prompt for distance?"]) {
distanceStr = await utils.inputPrompt("distance?","number",distanceStr);
}
const distance = parseInt(distanceStr);
if(isNaN(distance)) {
return;
}
if(rememberLastDistance) {
settings["Default distance"].value = distance;
ea.setScriptSettings(settings);
}
const elements=ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements)
.filter(els => !(els.length === 1 && els[0].type ==="arrow")); // ignore individual arrows
const groups = topGroups.sort((lha,rha) => lha[0].x - rha[0].x);
for(var i=0; i<groups.length; i++) {
if(i > 0) {
const preGroup = groups[i-1];
const curGroup = groups[i];
const preLeft = Math.min(...preGroup.map(el => el.x));
const preRight = Math.max(...preGroup.map(el => el.x + el.width));
const preCenter = preLeft + (preRight - preLeft) / 2;
const curLeft = Math.min(...curGroup.map(el => el.x));
const curRight = Math.max(...curGroup.map(el => el.x + el.width));
const curCenter = curLeft + (curRight - curLeft) / 2;
const distanceBetweenCenters = curCenter - preCenter - distance;
for(const curEl of curGroup) {
curEl.x = curEl.x - distanceBetweenCenters;
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);

View File

@@ -0,0 +1,126 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-inner-distance.png)
This script arranges selected elements and groups with a fixed inner distance.
Tips: You can use the `Box Selected Elements` and `Dimensions` scripts to create rectangles of the desired size, then use the `Change shape of selected elements` script to convert the rectangles to ellipses, and then use the `Fixed inner distance` script regains a desired inner distance.
Inspiration: #394
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```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["Default distance"]) {
settings = {
"Prompt for distance?": true,
"Default distance" : {
value: 10,
description: "Fixed horizontal distance between centers"
},
"Remember last distance?": false
};
ea.setScriptSettings(settings);
}
let distanceStr = settings["Default distance"].value.toString();
const rememberLastDistance = settings["Remember last distance?"];
if(settings["Prompt for distance?"]) {
distanceStr = await utils.inputPrompt("distance?","number",distanceStr);
}
const borders = ["top", "bottom", "left", "right"];
const fromBorder = await utils.suggester(borders, borders, "from border?");
if(!fromBorder) {
return;
}
const distance = parseInt(distanceStr);
if(isNaN(distance)) {
return;
}
if(rememberLastDistance) {
settings["Default distance"].value = distance;
ea.setScriptSettings(settings);
}
const elements=ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements)
.filter(els => !(els.length === 1 && els[0].type ==="arrow")); // ignore individual arrows
if(topGroups.length <= 1) {
new Notice("At least 2 or more elements or groups should be selected.");
return;
}
if(fromBorder === 'top') {
const groups = topGroups.sort((lha,rha) => Math.min(...lha.map(t => t.y)) - Math.min(...rha.map(t => t.y)));
const firstGroupTop = Math.min(...groups[0].map(el => el.y));
for(var i=0; i<groups.length; i++) {
if(i > 0) {
const curGroup = groups[i];
const moveDistance = distance * i;
for(const curEl of curGroup) {
curEl.y = firstGroupTop + moveDistance;
}
}
}
}
else if(fromBorder === 'bottom') {
const groups = topGroups.sort((lha,rha) => Math.min(...lha.map(t => t.y + t.height)) - Math.min(...rha.map(t => t.y + t.height))).reverse();
const firstGroupBottom = Math.max(...groups[0].map(el => el.y + el.height));
for(var i=0; i<groups.length; i++) {
if(i > 0) {
const curGroup = groups[i];
const moveDistance = distance * i;
for(const curEl of curGroup) {
curEl.y = firstGroupBottom - moveDistance - curEl.height;
}
}
}
}
else if(fromBorder === 'left') {
const groups = topGroups.sort((lha,rha) => Math.min(...lha.map(t => t.x)) - Math.min(...rha.map(t => t.x)));
const firstGroupLeft = Math.min(...groups[0].map(el => el.x));
for(var i=0; i<groups.length; i++) {
if(i > 0) {
const curGroup = groups[i];
const moveDistance = distance * i;
for(const curEl of curGroup) {
curEl.x = firstGroupLeft + moveDistance;
}
}
}
}
else if(fromBorder === 'right') {
const groups = topGroups.sort((lha,rha) => Math.min(...lha.map(t => t.x + t.width)) - Math.min(...rha.map(t => t.x + t.width))).reverse();
const firstGroupRight = Math.max(...groups[0].map(el => el.x + el.width));
for(var i=0; i<groups.length; i++) {
if(i > 0) {
const curGroup = groups[i];
const moveDistance = distance * i;
for(const curEl of curGroup) {
curEl.x = firstGroupRight - moveDistance - curEl.width;
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);

View File

@@ -11,13 +11,42 @@ When we create an architecture diagram or mind map, we often need to arrange a l
```javascript
*/
const spacing = parseInt (await utils.inputPrompt("spacing?","number","8"));
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 spacing"]) {
settings = {
"Prompt for spacing?": true,
"Default spacing" : {
value: 10,
description: "Fixed horizontal spacing between elements"
},
"Remember last spacing?": false
};
ea.setScriptSettings(settings);
}
let spacingStr = settings["Default spacing"].value.toString();
const rememberLastSpacing = settings["Remember last spacing?"];
if(settings["Prompt for spacing?"]) {
spacingStr = await utils.inputPrompt("spacing?","number",spacingStr);
}
const spacing = parseInt(spacingStr);
if(isNaN(spacing)) {
return;
}
const elements=ea.getViewSelectedElements()
if(rememberLastSpacing) {
settings["Default spacing"].value = spacing;
ea.setScriptSettings(settings);
}
const elements=ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements)
.filter(els => !(els.length === 1 && els[0].type ==="arrow")); // ignore individual arrows
const topGroups = ea.getMaximumGroups(elements);
const groups = topGroups.sort((lha,rha) => lha[0].x - rha[0].x);
for(var i=0; i<groups.length; i++) {

View File

@@ -0,0 +1,74 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-vertical-distance-between-centers.png)
This script arranges the selected elements vertically with a fixed center spacing.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```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["Default distance"]) {
settings = {
"Prompt for distance?": true,
"Default distance" : {
value: 10,
description: "Fixed vertical distance between centers"
},
"Remember last distance?": false
};
ea.setScriptSettings(settings);
}
let distanceStr = settings["Default distance"].value.toString();
const rememberLastDistance = settings["Remember last distance?"];
if(settings["Prompt for distance?"]) {
distanceStr = await utils.inputPrompt("distance?","number",distanceStr);
}
const distance = parseInt(distanceStr);
if(isNaN(distance)) {
return;
}
if(rememberLastDistance) {
settings["Default distance"].value = distance;
ea.setScriptSettings(settings);
}
const elements=ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements)
.filter(els => !(els.length === 1 && els[0].type ==="arrow")); // ignore individual arrows
const groups = topGroups.sort((lha,rha) => lha[0].y - rha[0].y);
for(var i=0; i<groups.length; i++) {
if(i > 0) {
const preGroup = groups[i-1];
const curGroup = groups[i];
const preTop = Math.min(...preGroup.map(el => el.y));
const preBottom = Math.max(...preGroup.map(el => el.y + el.height));
const preCenter = preTop + (preBottom - preTop) / 2;
const curTop = Math.min(...curGroup.map(el => el.y));
const curBottom = Math.max(...curGroup.map(el => el.y + el.height));
const curCenter = curTop + (curBottom - curTop) / 2;
const distanceBetweenCenters = curCenter - preCenter - distance;
for(const curEl of curGroup) {
curEl.y = curEl.y - distanceBetweenCenters;
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);

View File

@@ -11,10 +11,39 @@ When we create an architecture diagram or mind map, we often need to arrange a l
```javascript
*/
const spacing = parseInt (await utils.inputPrompt("spacing?","number","8"));
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 spacing"]) {
settings = {
"Prompt for spacing?": true,
"Default spacing" : {
value: 10,
description: "Fixed vertical spacing between elements"
},
"Remember last spacing?": false
};
ea.setScriptSettings(settings);
}
let spacingStr = settings["Default spacing"].value.toString();
const rememberLastSpacing = settings["Remember last spacing?"];
if(settings["Prompt for spacing?"]) {
spacingStr = await utils.inputPrompt("spacing?","number",spacingStr);
}
const spacing = parseInt(spacingStr);
if(isNaN(spacing)) {
return;
}
if(rememberLastSpacing) {
settings["Default spacing"].value = spacing;
ea.setScriptSettings(settings);
}
const elements=ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements)
.filter(els => !(els.length === 1 && els[0].type ==="arrow")); // ignore individual arrows

View File

@@ -13,10 +13,39 @@ Although excalidraw has the opacity option in its native property Settings, it a
```javascript
*/
const alpha = parseFloat(await utils.inputPrompt("Background color opacity?","number","0.6"));
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 opacity"]) {
settings = {
"Prompt for opacity?": true,
"Default opacity" : {
value: 0.6,
description: "Element's background color transparency"
},
"Remember last opacity?": false
};
ea.setScriptSettings(settings);
}
let opacityStr = settings["Default opacity"].value.toString();
const rememberLastOpacity = settings["Remember last opacity?"];
if(settings["Prompt for opacity?"]) {
opacityStr = await utils.inputPrompt("Background color opacity?","number",opacityStr);
}
const alpha = parseFloat(opacityStr);
if(isNaN(alpha)) {
return;
}
if(rememberLastOpacity) {
settings["Default opacity"].value = alpha;
ea.setScriptSettings(settings);
}
const elements=ea.getViewSelectedElements().filter((el)=>["rectangle","ellipse","diamond","line","image"].includes(el.type));
ea.copyViewElementsToEAforEditing(elements);
ea.getElements().forEach((el)=>{

View File

@@ -1,81 +1,74 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-normalize-selected-arrows.png)
This script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.
Tips: If you are drawing a flowchart, you can use `Normalize Selected Arrows` script to correct the position of the start and end points of the arrows, then use `Elbow connectors` script, and you will get the perfect connecting line!
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const selectedIndividualArrows = ea.getMaximumGroups(ea.getViewSelectedElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
const allElements = ea.getViewElements();
for(const arrow of selectedIndividualArrows) {
const startBindingEl = allElements.filter(el => el.id === (arrow.startBinding||{}).elementId)[0];
const endBindingEl = allElements.filter(el => el.id === (arrow.endBinding||{}).elementId)[0];
if(startBindingEl) {
recalculateStartPointOfLine(arrow, startBindingEl, endBindingEl);
}
if(endBindingEl) {
recalculateEndPointOfLine(arrow, endBindingEl, startBindingEl);
}
}
ea.copyViewElementsToEAforEditing(selectedIndividualArrows);
ea.addElementsToView();
function recalculateStartPointOfLine(line, el, elB) {
const aX = el.x + el.width/2;
const bX = (line.points.length <=2 && elB) ? elB.x + elB.width/2 : line.x + line.points[1][0];
const aY = el.y + el.height/2;
const bY = (line.points.length <=2 && elB) ? elB.y + elB.height/2 : line.y + line.points[1][1];
line.startBinding.gap = 8;
line.startBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.startBinding.gap
);
if(intersectA.length > 0) {
line.points[0] = [0, 0];
for(var i = 1; i<line.points.length; i++) {
line.points[i][0] -= intersectA[0][0] - line.x;
line.points[i][1] -= intersectA[0][1] - line.y;
}
line.x = intersectA[0][0];
line.y = intersectA[0][1];
}
}
function recalculateEndPointOfLine(line, el, elB) {
const aX = el.x + el.width/2;
const bX = (line.points.length <=2 && elB) ? elB.x + elB.width/2 : line.x + line.points[line.points.length-2][0];
const aY = el.y + el.height/2;
const bY = (line.points.length <=2 && elB) ? elB.y + elB.height/2 : line.y + line.points[line.points.length-2][1];
line.endBinding.gap = 8;
line.endBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.endBinding.gap
);
if(intersectA.length > 0) {
line.points[line.points.length - 1] = [intersectA[0][0] - line.x, intersectA[0][1] - line.y];
}
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-normalize-selected-arrows.png)
This script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.
Tips: If you are drawing a flowchart, you can use `Normalize Selected Arrows` script to correct the position of the start and end points of the arrows, then use `Elbow connectors` script, and you will get the perfect connecting line!
```javascript
*/
const selectedIndividualArrows = ea.getMaximumGroups(ea.getViewSelectedElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
const allElements = ea.getViewElements();
for(const arrow of selectedIndividualArrows) {
const startBindingEl = allElements.filter(el => el.id === (arrow.startBinding||{}).elementId)[0];
const endBindingEl = allElements.filter(el => el.id === (arrow.endBinding||{}).elementId)[0];
if(startBindingEl) {
recalculateStartPointOfLine(arrow, startBindingEl, endBindingEl);
}
if(endBindingEl) {
recalculateEndPointOfLine(arrow, endBindingEl, startBindingEl);
}
}
ea.copyViewElementsToEAforEditing(selectedIndividualArrows);
ea.addElementsToView();
function recalculateStartPointOfLine(line, el, elB) {
const aX = el.x + el.width/2;
const bX = (line.points.length <=2 && elB) ? elB.x + elB.width/2 : line.x + line.points[1][0];
const aY = el.y + el.height/2;
const bY = (line.points.length <=2 && elB) ? elB.y + elB.height/2 : line.y + line.points[1][1];
line.startBinding.gap = 8;
line.startBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.startBinding.gap
);
if(intersectA.length > 0) {
line.points[0] = [0, 0];
for(var i = 1; i<line.points.length; i++) {
line.points[i][0] -= intersectA[0][0] - line.x;
line.points[i][1] -= intersectA[0][1] - line.y;
}
line.x = intersectA[0][0];
line.y = intersectA[0][1];
}
}
function recalculateEndPointOfLine(line, el, elB) {
const aX = el.x + el.width/2;
const bX = (line.points.length <=2 && elB) ? elB.x + elB.width/2 : line.x + line.points[line.points.length-2][0];
const aY = el.y + el.height/2;
const bY = (line.points.length <=2 && elB) ? elB.y + elB.height/2 : line.y + line.points[line.points.length-2][1];
line.endBinding.gap = 8;
line.endBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.endBinding.gap
);
if(intersectA.length > 0) {
line.points[line.points.length - 1] = [intersectA[0][0] - line.x, intersectA[0][1] - line.y];
}
}

View File

@@ -16,12 +16,12 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.24")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
let token = ea.getScriptSettings().token.value??ea.getScriptSettings().token;
let token = ea.getScriptSettings().token?.value??ea.getScriptSettings().token;
const BASE_URL = "https://ocr.taskbone.com";
//convert setting to 1.5.21 format
@@ -110,15 +110,11 @@ console.log({text});
//add text element to drawing
const id = ea.addText(selectedImageElement.x,selectedImageElement.y+selectedImageElement.height,text);
await ea.addElementsToView();
const API = ea.getExcalidrawAPI();
st = API.getAppState();
st.selectedElementIds = {};
st.selectedElementIds[id] = true;
API.updateScene({appState: st});
API.zoomToFit(ea.getViewSelectedElements(),1);
ea.selectElementsInView([ea.getElement(id)]);
ea.getExcalidrawAPI().zoomToFit(ea.getViewSelectedElements(),1);
//utility function
function notice(message) {
new Notice(message,10000);
console.log(message);
}
}

View File

@@ -16,33 +16,39 @@ Open the script you are interested in and save it to your Obsidian Vault includi
|----|----|----|----|
|[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.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-bullet-point.jpg)|[@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.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-link-and-open.jpg)|[@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)|
|[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.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-each-selected-groups.png)|[@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.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-elements.jpg)|[@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).|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-connect-elements.jpg)|[@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|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-change-shape.jpg)|[@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).|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-connect-elements.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Convert freedraw to line](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20freedraw%20to%20line.md)|Convert selected freedraw objects into editable lines. This will allow you to adjust your drawings by dragging line points and will also allow you to select shape fill in case of enclosed lines. You can adjust conversion point density in settings|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-convert-freedraw-to-line.jpg)|[@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.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-textelement-to-transparent-stickynote.png)|[@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.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-copy-selected-element-styles-to-global.png)|[@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.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-create-and-embed-new-markdown-file.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Darken background color](Darken%20background%20color.md)|This script darkens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect. In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png)|[@1-2-3](https://github.com/1-2-3)|
|[Set Dimensions](Set%20Dimensions.md)|Currently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-dimensions.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Elbow connectors](Elbow%20connectors.md)|This script converts the selected connectors to elbows.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/elbow-connectors.png)|[@1-2-3](https://github.com/1-2-3)|
|[Expand rectangles horizontally keep text centered](Expand%20rectangles%20horizontally%20keep%20text20%centered.md)|This script expands the width of the selected rectangles until they are all the same width and keep the text centered.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)|[@1-2-3](https://github.com/1-2-3)|
|[Expand rectangles horizontally](Expand%20rectangles%20horizontally.md)|This script expands the width of the selected rectangles until they are all the same width.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)|[@1-2-3](https://github.com/1-2-3)|
|[Expand rectangles vertically keep text centered](Expand%20rectangles%20vertically%20keep%20text%20centered.md)|This script expands the height of the selected rectangles until they are all the same height and keep the text centered.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)|[@1-2-3](https://github.com/1-2-3)|
|[Expand rectangles vertically](Expand%20rectangles%20vertically.md)|This script expands the height of the selected rectangles until they are all the same height.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)|[@1-2-3](https://github.com/1-2-3)|
|[Fixed horizontal distance between centers](Fixed%20horizontal%20distance%20between%20centers.md)|This script arranges the selected elements horizontally with a fixed center spacing.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-horizontal-distance-between-centers.png)|[@1-2-3](https://github.com/1-2-3)|
|[Fixed inner distance](Fixed%20inner%20distance.md)|This script arranges selected elements and groups with a fixed inner distance.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-inner-distance.png)|[@1-2-3](https://github.com/1-2-3)|
|[Fixed spacing](Fixed%20spacing.md)|The script arranges the selected elements horizontally with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fix-space-demo.png)|[@1-2-3](https://github.com/1-2-3)|
|[Fixed vertical distance between centers](Fixed%20vertical%20distance%20between%20centers.md)|This script arranges the selected elements vertically with a fixed center spacing.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-vertical-distance-between-centers.png)|[@1-2-3](https://github.com/1-2-3)|
|[Fixed vertical distance](Fixed%20vertical%20distance.md)|The script arranges the selected elements vertically with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-vertical-distance.png)|[@1-2-3](https://github.com/1-2-3)|
|[Set Font Family](Set%20Font%20Family.md)|Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-font-family.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Grid](Set%20Grid.md)|The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Lighten background color](Lighten%20background%20color.md)|This script lightens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect.In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png)|[@1-2-3](https://github.com/1-2-3)|
|[Modify background color opacity](Modify%20background%20color%20opacity.md)|This script changes the opacity of the background color of the selected boxes. The default background color in Excalidraw is so dark that the text is hard to read. You can lighten the color a bit by setting transparency. And you can tweak the transparency over and over again until you're happy with it. Although excalidraw has the opacity option in its native property Settings, it also changes the transparency of the border. Use this script to change only the opacity of the background color without affecting the border.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-modify-background-color-opacity.png)|[@1-2-3](https://github.com/1-2-3)|
|[Set stroke width of selected elements](Set%20Stroke%20Width%20of%20Selected%20Elements.md)|This script will set the stroke width of selected elements. This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-stroke-width.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Normalize Selected Arrows](Normalize%20Selected%20Arrows.md)|This script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-normalize-selected-arrows.png)|[@1-2-3](https://github.com/1-2-3)|
|[OCR - Optical Character Recognition](OCR%20-%20Optical%20Character%20Recognition.md)|The script will 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and 2) will add the text to your drawing as a text element.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-ocr.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Reverse arrows](Reverse%20arrows.md)|Reverse the direction of **arrows** within the scope of selected elements.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-reverse-arrow.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Select Elements of Type](Select%20Elements%20of%20Type.md)|Prompts you with a list of the different element types in the active image. Only elements of the selected type will be selected on the canvas. If nothing is selected when running the script, then the script will process all the elements on the canvas. If some elements are selected when the script is executed, then the script will only process the selected elements.<br>The script is useful when, for example, you want to bring to front all the arrows, or want to change the color of all the text elements, etc.|![]('https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-select-element-of-type.jpg')|[@zsviczian](https://github.com/zsviczian)|
|[Set background color of unclosed line object by adding a shadow clone](Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.md)|Use this script to set the background color of unclosed (i.e. open) line objects by creating a clone of the object. The script will set the stroke color of the clone to transparent and will add a straight line to close the object. Use settings to define the default background color, the fill style, and the strokeWidth of the clone. By default the clone will be grouped with the original object, you can disable this also in settings.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-dimensions.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Dimensions](Set%20Dimensions.md)|Currently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-dimensions.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Font Family](Set%20Font%20Family.md)|Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-font-family.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Grid](Set%20Grid.md)|The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Link Alias](Set20%Link20%Alias.md)|Iterates all of the links in the selected TextElements and prompts the user to set or modify the alias for each link found.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-set-link-alias.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set stroke width of selected elements](Set%20Stroke%20Width%20of%20Selected%20Elements.md)|This script will set the stroke width of selected elements. This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-stroke-width.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Split text by lines](Split%20text%20by%20lines.md)|Split lines of text into separate text elements for easier reorganization|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-split-lines.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Text Alignment](Set%20Text%20Alignment.md)|Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-align.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Transfer TextElements to Excalidraw markdown metadata](Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md)|The script will delete the selected text elements from the canvas and will copy the text from these text elements into the Excalidraw markdown file as metadata. This means, that the text will no longer be visible in the drawing, however you will be able to search for the text in Obsidian and find the drawing containing this image.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-to-metadata.jpg)|[@zsviczian](https://github.com/zsviczian)|

View File

@@ -0,0 +1,47 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-select-element-of-type.jpg)
Prompts you with a list of the different element types in the active image. Only elements of the selected type will be selected on the canvas. If nothing is selected when running the script, then the script will process all the elements on the canvas. If some elements are selected when the script is executed, then the script will only process the selected elements.
The script is useful when, for example, you want to bring to front all the arrows, or want to change the color of all the text elements, etc.
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.24")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
let elements = ea.getViewSelectedElements();
if(elements.length === 0) elements = ea.getViewElements();
if(elements.length === 0) {
new Notice("There are no elements in the view");
return;
}
typeSet = new Set();
elements.forEach(el=>typeSet.add(el.type));
let elementType = Array.from(typeSet)[0];
if(typeSet.size > 1) {
elementType = await utils.suggester(
Array.from(typeSet).map((item) => {
switch(item) {
case "line": return "— line";
case "ellipse": return "○ ellipse";
case "rectangle": return "□ rectangle";
case "diamond": return "◇ diamond";
case "arrow": return "→ arrow";
case "freedraw": return "✎ freedraw";
case "image": return "🖼 image";
case "text": return "A text";
default: return item;
}
}),
Array.from(typeSet)
);
}
if(!elementType) return;
ea.selectElementsInView(elements.filter(el=>el.type === elementType));

View File

@@ -0,0 +1,80 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-set-background-color-of-unclosed-line.jpg)
Use this script to set the background color of unclosed (i.e. open) line objects by creating a clone of the object. The script will set the stroke color of the clone to transparent and will add a straight line to close the object. Use settings to define the default background color, the fill style, and the strokeWidth of the clone. By default the clone will be grouped with the original object, you can disable this also in settings.
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.26")) {
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["Background Color"]) {
settings = {
"Background Color" : {
value: "DimGray",
description: "Default background color of the 'shadow' object. Any valid html css color value",
},
"Fill Style": {
value: "hachure",
valueset: ["hachure","cross-hatch","solid"],
description: "Default fill style of the 'shadow' object."
},
"Inherit fill stroke width": {
value: true,
description: "This will impact the densness of the hachure or cross-hatch fill. Use the stroke width of the line object for which the shadow is created. If set to false, the script will use a stroke width of 2."
},
"Group 'shadow' with original": {
value: true,
description: "If the toggle is on then the shadow object that is created will be grouped with the unclosed original object."
}
};
ea.setScriptSettings(settings);
}
const inheritStrokeWidth = settings["Inherit fill stroke width"].value;
const backgroundColor = settings["Background Color"].value;
const fillStyle = settings["Fill Style"].value;
const shouldGroup = settings["Group 'shadow' with original"].value;
const elements = ea.getViewSelectedElements().filter(el=>el.type==="line");
if(elements.length === 0) {
new Notice("No line object is selected");
}
ea.copyViewElementsToEAforEditing(elements);
elementsToMove = [];
elements.forEach((el)=>{
const newEl = ea.cloneElement(el);
ea.elementsDict[newEl.id] = newEl;
newEl.roughness = 1;
if(!inheritStrokeWidth) newEl.strokeWidth = 2;
newEl.strokeColor = "transparent";
newEl.backgroundColor = backgroundColor;
newEl.fillStyle = fillStyle;
const i = el.points.length-1;
newEl.points.push([
//adding an extra point close to the last point in case distance is long from last point to origin and there is a sharp bend. This will avoid a spike due to a tight curve.
el.points[i][0]*0.9,
el.points[i][1]*0.9,
]);
newEl.points.push([0,0]);
if(shouldGroup) ea.addToGroup([el.id,newEl.id]);
elementsToMove.push({fillId: newEl.id, shapeId: el.id});
});
await ea.addElementsToView();
elementsToMove.forEach((x)=>{
const viewElements = ea.getViewElements();
ea.moveViewElementToZIndex(
x.fillId,
viewElements.indexOf(viewElements.filter(el=>el.id === x.shapeId)[0])-1
)
});
ea.selectElementsInView(ea.getElements());

View File

@@ -32,6 +32,7 @@ I would love to include your contribution in the script library. If you have a s
- [[#Box Selected Elements]]
- [[#Change shape of selected elements]]
- [[#Connect elements]]
- [[#Convert freedraw to line]]
- [[#Convert selected text elements to sticky notes]]
- [[#Convert text to link with folder and alias]]
- [[#Copy Selected Element Styles to Global]]
@@ -42,13 +43,18 @@ I would love to include your contribution in the script library. If you have a s
- [[#Expand rectangles horizontally]]
- [[#Expand rectangles vertically keep text centered]]
- [[#Expand rectangles vertically]]
- [[#Fixed horizontal distance between centers]]
- [[#Fixed inner distance]]
- [[#Fixed spacing]]
- [[#Fixed vertical distance between centers]]
- [[#Fixed vertical distance]]
- [[#Lighten background color]]
- [[#Modify background color opacity]]
- [[#Normalize Selected Arrows]]
- [[#OCR - Optical Character Recognition]]
- [[#Reverse arrows]]
- [[#Select Elements of Type]]
- [[#Set background color of unclosed line object by adding a shadow clone]]
- [[#Set Dimensions]]
- [[#Set Font Family]]
- [[#Set Grid]]
@@ -69,7 +75,7 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Link%20and%20Open%20Page.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Add%20Link%20and%20Open%20Page.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-link-and-open.jpg'></td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Add%20Link%20and%20Open%20Page.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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. Creates empty markdown file by default, this can be changed to creating a drawing by default via settings.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-link-and-open.jpg'></td></tr></table>
## Add Next Step in Process
```excalidraw-script-install
@@ -101,6 +107,12 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Connect%20elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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).<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-connect-elements.jpg'></td></tr></table>
## Convert freedraw to line
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20freedraw%20to%20line.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Convert%20freedraw%20to%20line.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Convert selected freedraw objects into editable lines. This will allow you to adjust your drawings by dragging line points and will also allow you to select shape fill in case of enclosed lines. You can adjust conversion point density in settings.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-convert-freedraw-to-line.jpg'></td></tr></table>
## Convert selected text elements to sticky notes
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20selected%20text%20elements%20to%20sticky%20notes.md
@@ -161,12 +173,30 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Expand%20rectangles%20vertically.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script expands the height of the selected rectangles until they are all the same height.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif'></td></tr></table>
## Fixed horizontal distance between centers
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20horizontal%20distance%20between%20centers.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Fixed%20horizontal%20distance%20between%20centers.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script arranges the selected elements horizontally with a fixed center spacing.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-horizontal-distance-between-centers.png'></td></tr></table>
## Fixed inner distance
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20inner%20distance.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Fixed%20inner%20distance.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script arranges selected elements and groups with a fixed inner distance.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-inner-distance.png'></td></tr></table>
## Fixed spacing
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20spacing.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Fixed%20spacing.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script arranges the selected elements horizontally with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fix-space-demo.png'></td></tr></table>
## Fixed vertical distance between centers
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20vertical%20distance%20between%20centers.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Fixed%20vertical%20distance%20between%20centers.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script arranges the selected elements vertically with a fixed center spacing.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-vertical-distance-between-centers.png'></td></tr></table>
## Fixed vertical distance
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20vertical%20distance.md
@@ -187,9 +217,9 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
## Normalize Selected Arrows
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Normalize%20Selected%20Arrows
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Normalize%20Selected%20Arrows.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Normalize%20Selected%20Arrows'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-normalize-selected-arrows.png'></td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Normalize%20Selected%20Arrows.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-normalize-selected-arrows.png'></td></tr></table>
## OCR - Optical Character Recognition
```excalidraw-script-install
@@ -203,6 +233,18 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Reverse%20arrows.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Reverse the direction of **arrows** within the scope of selected elements.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-reverse-arrow.jpg'></td></tr></table>
## Select Elements of Type
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Select%20Elements%20of%20Type.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Select%20Elements%20of%20Type.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Prompts you with a list of the different element types in the active image. Only elements of the selected type will be selected on the canvas. If nothing is selected when running the script, then the script will process all the elements on the canvas. If some elements are selected when the script is executed, then the script will only process the selected elements.<br>The script is useful when, for example, you want to bring to front all the arrows, or want to change the color of all the text elements, etc.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-select-element-of-type.jpg'></td></tr></table>
## Set background color of unclosed line object by adding a shadow clone
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Use this script to set the background color of unclosed (i.e. open) line objects by creating a clone of the object. The script will set the stroke color of the clone to transparent and will add a straight line to close the object. Use settings to define the default background color, the fill style, and the strokeWidth of the clone. By default the clone will be grouped with the original object, you can disable this also in settings.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-set-background-color-of-unclosed-line.jpg'></td></tr></table>
## Set Dimensions
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Dimensions.md

View File

@@ -1,4 +1,3 @@
{
"minifyWhitespace": true,
"minifySyntax":true
"minify": true
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "1.5.21",
"version": "1.5.30",
"minAppVersion": "0.12.16",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",

View File

@@ -12,7 +12,7 @@
"author": "",
"license": "MIT",
"dependencies": {
"@zsviczian/excalidraw": "0.10.0-obsidian-37",
"@zsviczian/excalidraw": "0.10.0-obsidian-39",
"monkey-around": "^2.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -31,6 +31,7 @@
"@types/js-beautify": "^1.13.3",
"@types/node": "^15.12.4",
"@types/react-dom": "^17.0.11",
"@popperjs/core": "^2.11.2",
"cross-env": "^7.0.3",
"html2canvas": "^1.4.0",
"nanoid": "^3.1.31",

View File

@@ -15,12 +15,10 @@ export default {
dir: '.',
sourcemap: 'inline',
format: 'cjs',
exports: 'default'
exports: 'default',
},
external: ['obsidian'],
plugins: [
typescript({inlineSources: !isProd}),
nodeResolve({ browser: true, preferBuiltins: true }),
replace({
preventAssignment: true,
"process.env.NODE_ENV": JSON.stringify(env.NODE_ENV),
@@ -29,6 +27,8 @@ export default {
exclude: "node_modules/**"
}),
commonjs(),
nodeResolve({ browser: true, preferBuiltins: true }),
typescript({inlineSources: !isProd}),
visualizer(),
]
],
};

View File

@@ -36,6 +36,7 @@ import {
getMaximumGroups,
intersectElementWithLine,
} from "@zsviczian/excalidraw";
import { stringify } from "querystring";
declare type ConnectionPoint = "top" | "bottom" | "left" | "right" | null;
const GAP = 4;
@@ -229,6 +230,10 @@ export interface ExcalidrawAutomate {
//recommended use:
//if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.20")) {new Notice("message");return;}
verifyMinimumPluginVersion(requiredVersion: string): boolean;
selectElementsInView(elements: ExcalidrawElement[]): void; //sets selection in view
generateElementId(): string; //returns an 8 character long random id
cloneElement(element: ExcalidrawElement): ExcalidrawElement; //Returns a clone of the element with a new id
moveViewElementToZIndex(elementId:number, newZIndex:number): void; //Moves the element to a specific position in the z-index
}
declare let window: any;
@@ -786,12 +791,15 @@ export async function initExcalidrawAutomate(
: 0.1,
gap: GAP,
},
startArrowhead: formatting?.startArrowHead
? formatting.startArrowHead
: this.style.startArrowHead,
endArrowhead: formatting?.endArrowHead
? formatting.endArrowHead
: this.style.endArrowHead,
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/388
startArrowhead:
typeof formatting?.startArrowHead !== "undefined"
? formatting.startArrowHead
: this.style.startArrowHead,
endArrowhead:
typeof formatting?.endArrowHead !== "undefined"
? formatting.endArrowHead
: this.style.endArrowHead,
...boxedElement(id, "arrow", points[0][0], points[0][1], box.w, box.h),
};
if (formatting?.startObjectId) {
@@ -1298,6 +1306,53 @@ export async function initExcalidrawAutomate(
const manifest = this.plugin.app.plugins.manifests[PLUGIN_ID];
return manifest.version >= requiredVersion;
},
selectElementsInView(elements: ExcalidrawElement[]):void {
if (!this.targetView || !this.targetView?._loaded) {
errorMessage("targetView not set", "selectElementsInView()");
return;
}
if (!elements || elements.length===0) {
return;
}
const API = this.getExcalidrawAPI();
API.selectElements(elements);
},
generateElementId(): string {
return nanoid();
},
cloneElement(element: ExcalidrawElement): ExcalidrawElement{
const newEl = JSON.parse(JSON.stringify(element));
newEl.id = nanoid();
return newEl;
},
moveViewElementToZIndex(elementId:number, newZIndex:number): void {
if (!this.targetView || !this.targetView?._loaded) {
errorMessage("targetView not set", "moveViewElementToZIndex()");
return;
}
const API = this.getExcalidrawAPI();
const elements = this.getViewElements();
const elementToMove = elements.filter((el:any)=>el.id===elementId);
if (elementToMove.length === 0) {
errorMessage(`Element (id: ${elementId}) not found`, "moveViewElementToZIndex");
return;
}
if (newZIndex >= elements.length) {
API.bringToFront(elementToMove);
return;
}
if (newZIndex < 0) {
API.sendToBack(elementToMove);
return;
}
const oldZIndex = elements.indexOf(elementToMove[0]);
elements.splice(newZIndex, 0, elements.splice(oldZIndex, 1)[0]);
API.updateScene({
elements: elements,
commitToHistory: true,
});
}
};
await initFonts();
return window.ExcalidrawAutomate;
@@ -1582,6 +1637,7 @@ export async function createSVG(
exportSettings?.withBackground ?? plugin.settings.exportWithBackground,
withTheme: exportSettings?.withTheme ?? plugin.settings.exportWithTheme,
},
plugin.settings.exportPaddingSVG,
);
if (template?.hasSVGwithBitmap) {
svg.setAttribute("hasbitmap", "true");

View File

@@ -5,7 +5,7 @@
originalText: this is the text without added linebreaks for wrapping. This will be parsed or markup depending on view mode
rawText: text with original markdown markup and without the added linebreaks for wrapping
*/
import { App, TFile } from "obsidian";
import { App, Notice, TFile } from "obsidian";
import {
nanoid,
FRONTMATTER_KEY_CUSTOM_PREFIX,
@@ -34,6 +34,7 @@ import {
} from "@zsviczian/excalidraw/types/element/types";
import { BinaryFiles, SceneData } from "@zsviczian/excalidraw/types/types";
import { EmbeddedFile } from "./EmbeddedFileLoader";
import { t } from "./lang/helpers";
type SceneDataWithFiles = SceneData & { files: BinaryFiles };
@@ -81,7 +82,8 @@ export const REGEX_LINK = {
export const REG_LINKINDEX_HYPERLINK = /^\w+:\/\//;
const DRAWING_REG = /\n# Drawing\n[^`]*(```json\n)([\s\S]*?)```/gm; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/182
//added \n at and of DRAWING_REG: https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/357
const DRAWING_REG = /\n# Drawing\n[^`]*(```json\n)([\s\S]*?)```\n/gm; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/182
const DRAWING_REG_FALLBACK = /\n# Drawing\n(```json\n)?(.*)(```)?(%%)?/gm;
export function getJSON(data: string): { scene: string; pos: number } {
let res = data.matchAll(DRAWING_REG);
@@ -105,11 +107,7 @@ export function getJSON(data: string): { scene: string; pos: number } {
}
export function getMarkdownDrawingSection(jsonString: string) {
return `%%\n# Drawing\n${String.fromCharCode(96)}${String.fromCharCode(
96,
)}${String.fromCharCode(96)}json\n${jsonString}\n${String.fromCharCode(
96,
)}${String.fromCharCode(96)}${String.fromCharCode(96)}\n%%`;
return `%%\n# Drawing\n\x60\x60\x60json\n${jsonString}\n\x60\x60\x60\n%%`;
}
/**
@@ -251,13 +249,30 @@ export class ExcalidrawData {
}
}
//Load scene: Read the JSON string after "# Drawing"
const sceneJSONandPOS = getJSON(data);
if (sceneJSONandPOS.pos === -1) {
return false; //JSON not found
}
if (!this.scene) {
this.scene = JSON_parse(sceneJSONandPOS.scene); //this is a workaround to address when files are mereged by sync and one version is still an old markdown without the codeblock ```
// https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/396
let sceneJSONandPOS = null;
const loadJSON = (): { scene: string; pos: number } => {
//Load scene: Read the JSON string after "# Drawing"
const sceneJSONandPOS = getJSON(data);
if (sceneJSONandPOS.pos === -1) {
throw new Error("Excalidraw JSON not found in the file");
}
if (!this.scene) {
this.scene = JSON_parse(sceneJSONandPOS.scene); //this is a workaround to address when files are mereged by sync and one version is still an old markdown without the codeblock ```
}
return sceneJSONandPOS;
};
try {
sceneJSONandPOS = loadJSON();
} catch (e) {
const bakfile = this.app.vault.getAbstractFileByPath(`${file.path}.bak`);
if (bakfile && bakfile instanceof TFile) {
data = await this.app.vault.read(bakfile);
sceneJSONandPOS = loadJSON();
new Notice(t("LOAD_FROM_BACKUP"), 4000);
} else {
throw e;
}
}
if (!this.scene.files) {
@@ -382,11 +397,6 @@ export class ExcalidrawData {
newOriginalText: string,
forceUpdate: boolean = false,
) {
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/376
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
}
if (forceUpdate || newText != sceneTextElement.text) {
const measure = measureText(
newText,
@@ -395,7 +405,13 @@ export class ExcalidrawData {
);
sceneTextElement.text = newText;
sceneTextElement.originalText = newOriginalText;
sceneTextElement.width = measure.w;
if (!sceneTextElement.containerId) {
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/376
//I leave the setting of text width to excalidraw, when text is in a container
//because text width is fixed to the container width
sceneTextElement.width = measure.w;
}
sceneTextElement.height = measure.h;
sceneTextElement.baseline = measure.baseline;
}

View File

@@ -62,7 +62,7 @@ import {
svgToBase64,
viewportCoordsToSceneCoords,
} from "./Utils";
import { Prompt } from "./Prompt";
import { NewFileActions, Prompt } from "./Prompt";
import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
import { updateEquation } from "./LaTeX";
import {
@@ -200,7 +200,11 @@ export default class ExcalidrawView extends TextFileView {
withBackground: this.plugin.settings.exportWithBackground,
withTheme: this.plugin.settings.exportWithTheme,
};
const svg = await getSVG(scene, exportSettings);
const svg = await getSVG (
scene,
exportSettings,
this.plugin.settings.exportPaddingSVG,
);
if (!svg) {
return;
}
@@ -252,6 +256,9 @@ export default class ExcalidrawView extends TextFileView {
if (!this.isLoaded) {
return;
}
if(!this.app.vault.getAbstractFileByPath(this.file.path)) {
return; //file was recently deleted
}
this.preventReload = preventReload;
this.dirty = null;
const scene = this.getScene();
@@ -265,7 +272,15 @@ export default class ExcalidrawView extends TextFileView {
//debug({where:"ExcalidrawView.save",file:this.file.name,dataTheme:this.excalidrawData.scene.appState.theme,before:"loadDrawing(false)"})
await this.loadDrawing(false);
}
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/396
const bakfilepath = `${this.file.path}.bak`;
if (await this.app.vault.adapter.exists(bakfilepath)) {
await this.app.vault.adapter.remove(bakfilepath);
}
await this.app.vault.adapter.copy(this.file.path, bakfilepath);
await super.save();
await this.app.vault.adapter.remove(bakfilepath);
if (!this.autosaving) {
if (this.plugin.settings.autoexportSVG) {
@@ -456,10 +471,6 @@ export default class ExcalidrawView extends TextFileView {
linkText,
view.file.path,
);
if (!ev.altKey && !file) {
new Notice(t("FILE_DOES_NOT_EXIST"), 4000);
return;
}
} else {
const selectedImage = this.getSelectedImageElement();
if (selectedImage?.id) {
@@ -515,10 +526,11 @@ export default class ExcalidrawView extends TextFileView {
}
linkText = this.excalidrawData.getFile(selectedImage.fileId).file
.path;
file = this.excalidrawData.getFile(selectedImage.fileId).file;
}
}
}
if (!linkText) {
new Notice(t("LINK_BUTTON_CLICK_NO_TEXT"), 20000);
return;
@@ -528,15 +540,15 @@ export default class ExcalidrawView extends TextFileView {
if (ev.shiftKey && this.isFullscreen()) {
this.exitFullscreen();
}
if (!file) {
(new NewFileActions(this.plugin,linkText,ev.shiftKey,view)).open();
return;
}
const leaf = ev.shiftKey
? getNewOrAdjacentLeaf(this.plugin, view.leaf)
: view.leaf;
view.app.workspace.setActiveLeaf(leaf);
if (file) {
leaf.openFile(file, { eState: { line: lineNum - 1 } }); //if file exists open file and jump to reference
} else {
leaf.view.app.workspace.openLinkText(linkText, view.file.path);
}
leaf.openFile(file, { eState: { line: lineNum - 1 } }); //if file exists open file and jump to reference
view.app.workspace.setActiveLeaf(leaf,true,true);
} catch (e) {
new Notice(e, 4000);
}
@@ -763,8 +775,8 @@ export default class ExcalidrawView extends TextFileView {
e.message === "Cannot read property 'index' of undefined"
? "\n'# Drawing' section is likely missing"
: ""
}\nTry manually fixing the file or restoring an earlier version from sync history`,
8000,
}\n\nTry manually fixing the file or restoring an earlier version from sync history.\n\nYou may also look for .bak file with last working version in the same folder. Note the .bak file might not get synchronized, so look for .bak file on other devices as well.`,
10000,
);
this.setMarkdownView();
return;
@@ -1009,7 +1021,11 @@ export default class ExcalidrawView extends TextFileView {
withBackground: this.plugin.settings.exportWithBackground,
withTheme: this.plugin.settings.exportWithTheme,
};
let svg = await getSVG(this.getScene(), exportSettings);
let svg = await getSVG (
this.getScene(),
exportSettings,
this.plugin.settings.exportPaddingSVG,
);
if (!svg) {
return null;
}
@@ -1942,7 +1958,7 @@ export default class ExcalidrawView extends TextFileView {
this.isEditingTextResetTimer = setTimeout(() => {
this.isEditingText = false;
this.isEditingTextResetTimer = null;
}, 300); // to give time for the onscreen keyboard to disappear
}, 1500); // to give time for the onscreen keyboard to disappear
if (isDeleted) {
this.excalidrawData.deleteTextElement(textElement.id);

123
src/FieldSuggestor.ts Normal file
View File

@@ -0,0 +1,123 @@
import {
Editor,
EditorPosition,
EditorSuggest,
EditorSuggestContext,
EditorSuggestTriggerInfo,
TFile,
} from "obsidian";
import { FRONTMATTER_KEYS_INFO } from "./SuggestorInfo";
import {
EXCALIDRAW_AUTOMATE_INFO,
EXCALIDRAW_SCRIPTENGINE_INFO,
} from "./SuggestorInfo";
import type ExcalidrawPlugin from "./main";
export class FieldSuggestor extends EditorSuggest<string> {
plugin: ExcalidrawPlugin;
suggestType: "ea" | "excalidraw" | "utils";
latestTriggerInfo: EditorSuggestTriggerInfo;
constructor(plugin: ExcalidrawPlugin) {
super(plugin.app);
this.plugin = plugin;
}
onTrigger(
cursor: EditorPosition,
editor: Editor,
_: TFile,
): EditorSuggestTriggerInfo | null {
if (this.plugin.settings.fieldSuggestor) {
const sub = editor.getLine(cursor.line).substring(0, cursor.ch);
const match =
sub.match(/^excalidraw-(.*)$/)?.[1] ??
sub.match(/(^ea|\Wea)\.([\w\.]*)$/)?.[2] ??
sub.match(/(^utils|\Wutils)\.([\w\.]*)$/)?.[2];
if (match !== undefined) {
this.suggestType = sub.match(/^excalidraw-(.*)$/)
? "excalidraw"
: sub.match(/(^ea|\Wea)\.([\w\.]*)$/)
? "ea"
: "utils";
this.latestTriggerInfo = {
end: cursor,
start: {
ch: cursor.ch - match.length,
line: cursor.line,
},
query: match,
};
return this.latestTriggerInfo;
}
}
return null;
}
getSuggestions = (context: EditorSuggestContext) => {
const query = context.query.toLowerCase();
const keys =
this.suggestType === "ea"
? EXCALIDRAW_AUTOMATE_INFO
: this.suggestType === "utils"
? EXCALIDRAW_SCRIPTENGINE_INFO
: FRONTMATTER_KEYS_INFO;
return keys
.map((sug) => sug.field)
.filter((sug) => sug.toLowerCase().includes(query));
};
renderSuggestion(suggestion: string, el: HTMLElement): void {
const text = suggestion.replace(
this.suggestType === "ea"
? "ea."
: this.suggestType === "utils"
? "utils."
: "excalidraw-",
"",
);
const keys =
this.suggestType === "ea"
? EXCALIDRAW_AUTOMATE_INFO
: this.suggestType === "utils"
? EXCALIDRAW_SCRIPTENGINE_INFO
: FRONTMATTER_KEYS_INFO;
const value = keys.find((f) => f.field === suggestion);
el.createEl("b", { text});
el.createEl("br");
if(value.code) {
el.createEl("code", { text: value.code });
}
if(value.desc) {
el.createDiv("div",el=>el.innerHTML=value.desc);
}
}
selectSuggestion(suggestion: string): void {
const { context } = this;
if (context) {
const keys =
this.suggestType === "ea"
? EXCALIDRAW_AUTOMATE_INFO
: this.suggestType === "utils"
? EXCALIDRAW_SCRIPTENGINE_INFO
: FRONTMATTER_KEYS_INFO;
const replacement = `${suggestion}${
keys.find((f) => f.field === suggestion)?.after
}`;
context.editor.replaceRange(
replacement,
this.latestTriggerInfo.start,
this.latestTriggerInfo.end,
);
if (this.latestTriggerInfo.start.ch === this.latestTriggerInfo.end.ch) {
// Dirty hack to prevent the cursor being at the
// beginning of the word after completion,
// Not sure what's the cause of this bug.
const cursor_pos = this.latestTriggerInfo.end;
cursor_pos.ch += replacement.length;
context.editor.setCursor(cursor_pos);
}
}
}
}

460
src/FolderSuggester.ts Normal file
View File

@@ -0,0 +1,460 @@
import {
FuzzyMatch,
TFile,
BlockCache,
HeadingCache,
CachedMetadata,
TextComponent,
App,
TFolder,
FuzzySuggestModal,
SuggestModal,
Scope
} from "obsidian";
import { t } from "./lang/helpers";
import { createPopper, Instance as PopperInstance } from "@popperjs/core";
class Suggester<T> {
owner: SuggestModal<T>;
items: T[];
suggestions: HTMLDivElement[];
selectedItem: number;
containerEl: HTMLElement;
constructor(
owner: SuggestModal<T>,
containerEl: HTMLElement,
scope: Scope
) {
this.containerEl = containerEl;
this.owner = owner;
containerEl.on(
"click",
".suggestion-item",
this.onSuggestionClick.bind(this)
);
containerEl.on(
"mousemove",
".suggestion-item",
this.onSuggestionMouseover.bind(this)
);
scope.register([], "ArrowUp", () => {
this.setSelectedItem(this.selectedItem - 1, true);
return false;
});
scope.register([], "ArrowDown", () => {
this.setSelectedItem(this.selectedItem + 1, true);
return false;
});
scope.register([], "Enter", (evt) => {
this.useSelectedItem(evt);
return false;
});
scope.register([], "Tab", (evt) => {
this.chooseSuggestion(evt);
return false;
});
}
chooseSuggestion(evt: KeyboardEvent) {
if (!this.items || !this.items.length) return;
const currentValue = this.items[this.selectedItem];
if (currentValue) {
this.owner.onChooseSuggestion(currentValue, evt);
}
}
onSuggestionClick(event: MouseEvent, el: HTMLDivElement): void {
event.preventDefault();
if (!this.suggestions || !this.suggestions.length) return;
const item = this.suggestions.indexOf(el);
this.setSelectedItem(item, false);
this.useSelectedItem(event);
}
onSuggestionMouseover(event: MouseEvent, el: HTMLDivElement): void {
if (!this.suggestions || !this.suggestions.length) return;
const item = this.suggestions.indexOf(el);
this.setSelectedItem(item, false);
}
empty() {
this.containerEl.empty();
}
setSuggestions(items: T[]) {
this.containerEl.empty();
const els: HTMLDivElement[] = [];
items.forEach((item) => {
const suggestionEl = this.containerEl.createDiv("suggestion-item");
this.owner.renderSuggestion(item, suggestionEl);
els.push(suggestionEl);
});
this.items = items;
this.suggestions = els;
this.setSelectedItem(0, false);
}
useSelectedItem(event: MouseEvent | KeyboardEvent) {
if (!this.items || !this.items.length) return;
const currentValue = this.items[this.selectedItem];
if (currentValue) {
this.owner.selectSuggestion(currentValue, event);
}
}
wrap(value: number, size: number): number {
return ((value % size) + size) % size;
}
setSelectedItem(index: number, scroll: boolean) {
const nIndex = this.wrap(index, this.suggestions.length);
const prev = this.suggestions[this.selectedItem];
const next = this.suggestions[nIndex];
if (prev) prev.removeClass("is-selected");
if (next) next.addClass("is-selected");
this.selectedItem = nIndex;
if (scroll) {
next.scrollIntoView(false);
}
}
}
export abstract class SuggestionModal<T> extends FuzzySuggestModal<T> {
items: T[] = [];
suggestions: HTMLDivElement[];
popper: PopperInstance;
//@ts-ignore
scope: Scope = new Scope(this.app.scope);
suggester: Suggester<FuzzyMatch<T>>;
suggestEl: HTMLDivElement;
promptEl: HTMLDivElement;
emptyStateText: string = "No match found";
limit: number = 100;
shouldNotOpen: boolean;
constructor(app: App, inputEl: HTMLInputElement, items: T[]) {
super(app);
this.inputEl = inputEl;
this.items = items;
this.suggestEl = createDiv("suggestion-container");
this.contentEl = this.suggestEl.createDiv("suggestion");
this.suggester = new Suggester(this, this.contentEl, this.scope);
this.scope.register([], "Escape", this.onEscape.bind(this));
this.inputEl.addEventListener("input", this.onInputChanged.bind(this));
this.inputEl.addEventListener("focus", this.onFocus.bind(this));
this.inputEl.addEventListener("blur", this.close.bind(this));
this.suggestEl.on(
"mousedown",
".suggestion-container",
(event: MouseEvent) => {
event.preventDefault();
}
);
}
empty() {
this.suggester.empty();
}
onInputChanged(): void {
if (this.shouldNotOpen) return;
const inputStr = this.modifyInput(this.inputEl.value);
const suggestions = this.getSuggestions(inputStr);
if (suggestions.length > 0) {
this.suggester.setSuggestions(suggestions.slice(0, this.limit));
} else {
this.onNoSuggestion();
}
this.open();
}
onFocus(): void {
this.shouldNotOpen = false;
this.onInputChanged();
}
modifyInput(input: string): string {
return input;
}
onNoSuggestion() {
this.empty();
this.renderSuggestion(
null,
this.contentEl.createDiv("suggestion-item")
);
}
open(): void {
// TODO: Figure out a better way to do this. Idea from Periodic Notes plugin
this.app.keymap.pushScope(this.scope);
document.body.appendChild(this.suggestEl);
this.popper = createPopper(this.inputEl, this.suggestEl, {
placement: "bottom-start",
modifiers: [
{
name: "offset",
options: {
offset: [0, 10]
}
},
{
name: "flip",
options: {
fallbackPlacements: ["top"]
}
}
]
});
}
onEscape(): void {
this.close();
this.shouldNotOpen = true;
}
close(): void {
// TODO: Figure out a better way to do this. Idea from Periodic Notes plugin
this.app.keymap.popScope(this.scope);
this.suggester.setSuggestions([]);
if (this.popper) {
this.popper.destroy();
}
this.suggestEl.detach();
}
createPrompt(prompts: HTMLSpanElement[]) {
if (!this.promptEl)
this.promptEl = this.suggestEl.createDiv("prompt-instructions");
let prompt = this.promptEl.createDiv("prompt-instruction");
for (let p of prompts) {
prompt.appendChild(p);
}
}
abstract onChooseItem(item: T, evt: MouseEvent | KeyboardEvent): void;
abstract getItemText(arg: T): string;
abstract getItems(): T[];
}
export class PathSuggestionModal extends SuggestionModal<
TFile | BlockCache | HeadingCache
> {
file: TFile;
files: TFile[];
text: TextComponent;
cache: CachedMetadata;
constructor(app: App, input: TextComponent, items: TFile[]) {
super(app, input.inputEl, items);
this.files = [...items];
this.text = input;
//this.getFile();
this.inputEl.addEventListener("input", this.getFile.bind(this));
}
getFile() {
const v = this.inputEl.value,
file = this.app.metadataCache.getFirstLinkpathDest(
v.split(/[\^#]/).shift() || "",
""
);
if (file == this.file) return;
this.file = file;
if (this.file)
this.cache = this.app.metadataCache.getFileCache(this.file);
this.onInputChanged();
}
getItemText(item: TFile | HeadingCache | BlockCache) {
if (item instanceof TFile) return item.path;
if (Object.prototype.hasOwnProperty.call(item, "heading")) {
return (<HeadingCache>item).heading;
}
if (Object.prototype.hasOwnProperty.call(item, "id")) {
return (<BlockCache>item).id;
}
}
onChooseItem(item: TFile | HeadingCache | BlockCache) {
if (item instanceof TFile) {
this.text.setValue(item.basename);
this.file = item;
this.cache = this.app.metadataCache.getFileCache(this.file);
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
this.text.setValue(
this.file.basename + "#" + (<HeadingCache>item).heading
);
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
this.text.setValue(
this.file.basename + "^" + (<BlockCache>item).id
);
}
}
selectSuggestion({ item }: FuzzyMatch<TFile | BlockCache | HeadingCache>) {
let link: string;
if (item instanceof TFile) {
link = item.basename;
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
link = this.file.basename + "#" + (<HeadingCache>item).heading;
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
link = this.file.basename + "^" + (<BlockCache>item).id;
}
this.text.setValue(link);
this.onClose();
this.close();
}
renderSuggestion(
result: FuzzyMatch<TFile | BlockCache | HeadingCache>,
el: HTMLElement
) {
let { item, match: matches } = result || {};
let content = el.createDiv({
cls: "suggestion-content"
});
if (!item) {
content.setText(this.emptyStateText);
content.parentElement.addClass("is-selected");
return;
}
if (item instanceof TFile) {
let pathLength = item.path.length - item.name.length;
const matchElements = matches.matches.map((m) => {
return createSpan("suggestion-highlight");
});
for (
let i = pathLength;
i < item.path.length - item.extension.length - 1;
i++
) {
let match = matches.matches.find((m) => m[0] === i);
if (match) {
let element = matchElements[matches.matches.indexOf(match)];
content.appendChild(element);
element.appendText(item.path.substring(match[0], match[1]));
i += match[1] - match[0] - 1;
continue;
}
content.appendText(item.path[i]);
}
el.createDiv({
cls: "suggestion-note",
text: item.path
});
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
content.setText((<HeadingCache>item).heading);
content.prepend(
createSpan({
cls: "suggestion-flair",
text: `H${(<HeadingCache>item).level}`
})
);
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
content.setText((<BlockCache>item).id);
}
}
get headings() {
if (!this.file) return [];
if (!this.cache) {
this.cache = this.app.metadataCache.getFileCache(this.file);
}
return this.cache.headings || [];
}
get blocks() {
if (!this.file) return [];
if (!this.cache) {
this.cache = this.app.metadataCache.getFileCache(this.file);
}
return Object.values(this.cache.blocks || {}) || [];
}
getItems() {
const v = this.inputEl.value;
if (/#/.test(v)) {
this.modifyInput = (i) => i.split(/#/).pop();
return this.headings;
} else if (/\^/.test(v)) {
this.modifyInput = (i) => i.split(/\^/).pop();
return this.blocks;
}
return this.files;
}
}
export class FolderSuggestionModal extends SuggestionModal<TFolder> {
text: TextComponent;
cache: CachedMetadata;
folders: TFolder[];
folder: TFolder;
constructor(app: App, input: TextComponent, items: TFolder[]) {
super(app, input.inputEl, items);
this.folders = [...items];
this.text = input;
this.inputEl.addEventListener("input", () => this.getFolder());
}
getFolder() {
const v = this.inputEl.value,
folder = this.app.vault.getAbstractFileByPath(v);
if (folder == this.folder) return;
if (!(folder instanceof TFolder)) return;
this.folder = folder;
this.onInputChanged();
}
getItemText(item: TFolder) {
return item.path;
}
onChooseItem(item: TFolder) {
this.text.setValue(item.path);
this.folder = item;
}
selectSuggestion({ item }: FuzzyMatch<TFolder>) {
let link = item.path;
this.text.setValue(link);
this.onClose();
this.close();
}
renderSuggestion(result: FuzzyMatch<TFolder>, el: HTMLElement) {
let { item, match: matches } = result || {};
let content = el.createDiv({
cls: "suggestion-content"
});
if (!item) {
content.setText(this.emptyStateText);
content.parentElement.addClass("is-selected");
return;
}
let pathLength = item.path.length - item.name.length;
const matchElements = matches.matches.map((m) => {
return createSpan("suggestion-highlight");
});
for (let i = pathLength; i < item.path.length; i++) {
let match = matches.matches.find((m) => m[0] === i);
if (match) {
let element = matchElements[matches.matches.indexOf(match)];
content.appendChild(element);
element.appendText(item.path.substring(match[0], match[1]));
i += match[1] - match[0] - 1;
continue;
}
content.appendText(item.path[i]);
}
el.createDiv({
cls: "suggestion-note",
text: item.path
});
}
getItems() {
return this.folders;
}
}

View File

@@ -52,6 +52,9 @@ const getIMG = async (
file = f;
}
// https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/387
imgAttributes.style = imgAttributes.style.replaceAll(" ", "-");
const exportSettings: ExportSettings = {
withBackground: plugin.settings.exportWithBackground,
withTheme: plugin.settings.exportWithTheme,
@@ -237,7 +240,6 @@ const processInternalEmbeds = async (
attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`;
}
}
attr.fname = file?.path;
attr.file = file;
const div = await createImageDiv(attr);

View File

@@ -6,7 +6,11 @@ import {
FuzzyMatch,
FuzzySuggestModal,
Instruction,
TFile,
} from "obsidian";
import ExcalidrawView from "./ExcalidrawView";
import ExcalidrawPlugin from "./main";
import { getNewOrAdjacentLeaf, sleep } from "./Utils";
export class Prompt extends Modal {
private promptEl: HTMLInputElement;
@@ -281,3 +285,131 @@ export class GenericSuggester extends FuzzySuggestModal<any> {
}
}
}
class MigrationPrompt extends Modal {
private plugin: ExcalidrawPlugin;
constructor(app: App, plugin: ExcalidrawPlugin) {
super(app);
this.plugin = plugin;
}
onOpen(): void {
this.titleEl.setText("Welcome to Excalidraw 1.2");
this.createForm();
}
onClose(): void {
this.contentEl.empty();
}
createForm(): void {
const div = this.contentEl.createDiv();
// div.addClass("excalidraw-prompt-div");
// div.style.maxWidth = "600px";
div.createEl("p", {
text: "This version comes with tons of new features and possibilities. Please read the description in Community Plugins to find out more.",
});
div.createEl("p", { text: "" }, (el) => {
el.innerHTML =
"Drawings you've created with version 1.1.x need to be converted to take advantage of the new features. You can also continue to use them in compatibility mode. " +
"During conversion your old *.excalidraw files will be replaced with new *.excalidraw.md files.";
});
div.createEl("p", { text: "" }, (el) => {
//files manually follow one of two options:
el.innerHTML =
"To convert your drawings you have the following options:<br><ul>" +
"<li>Click <code>CONVERT FILES</code> now to convert all of your *.excalidraw files, or if you prefer to make a backup first, then click <code>CANCEL</code>.</li>" +
"<li>In the Command Palette select <code>Excalidraw: Convert *.excalidraw files to *.excalidraw.md files</code></li>" +
"<li>Right click an <code>*.excalidraw</code> file in File Explorer and select one of the following options to convert files one by one: <ul>" +
"<li><code>*.excalidraw => *.excalidraw.md</code></li>" +
"<li><code>*.excalidraw => *.md (Logseq compatibility)</code>. This option will retain the original *.excalidraw file next to the new Obsidian format. " +
"Make sure you also enable <code>Compatibility features</code> in Settings for a full solution.</li></ul></li>" +
"<li>Open a drawing in compatibility mode and select <code>Convert to new format</code> from the <code>Options Menu</code></li></ul>";
});
div.createEl("p", {
text: "This message will only appear maximum 3 times in case you have *.excalidraw files in your Vault.",
});
const bConvert = div.createEl("button", { text: "CONVERT FILES" });
bConvert.onclick = () => {
this.plugin.convertExcalidrawToMD();
this.close();
};
const bCancel = div.createEl("button", { text: "CANCEL" });
bCancel.onclick = () => {
this.close();
};
}
}
export class NewFileActions extends Modal {
constructor (
private plugin: ExcalidrawPlugin,
private path: string,
private newPane: boolean,
private view: ExcalidrawView,
) {
super(plugin.app);
}
onOpen(): void {
this.createForm();
}
async onClose() {
}
openFile(file: TFile): void {
if(!file) return;
const leaf = this.newPane
? getNewOrAdjacentLeaf(this.plugin, this.view.leaf)
: this.view.leaf;
leaf.openFile(file);
this.app.workspace.setActiveLeaf(leaf, true, true);
}
createForm(): void {
this.titleEl.setText("New File");
this.contentEl.createDiv({
cls: "excalidraw-prompt-center",
text: "File does not exist. Do you want to create it?"
});
this.contentEl.createDiv({
cls: "excalidraw-prompt-center filepath",
text: this.path
});
this.contentEl.createDiv({cls: "excalidraw-prompt-center"}, (el) => {
//files manually follow one of two options:
el.style.textAlign = "right";
const bMd = el.createEl("button", { text: "Create Markdown" });
bMd.onclick = async () => {
//@ts-ignore
const f = await this.app.fileManager.createNewMarkdownFileFromLinktext(this.path,this.viewFile);
this.openFile(f);
this.close();
};
const bEx = el.createEl("button", { text: "Create Excalidraw" });
bEx.onclick = async () => {
//@ts-ignore
const f = await this.app.fileManager.createNewMarkdownFileFromLinktext(this.path,this.viewFile)
if(!f) return;
await this.app.vault.modify(f,await this.plugin.getBlankDrawing());
await sleep(200); //wait for metadata cache to update, so file opens as excalidraw
this.openFile(f);
this.close();
};
const bCancel = el.createEl("button", {
text: "Never Mind",
});
bCancel.onclick = () => {
this.close();
};
});
}
}

View File

@@ -1,9 +1,10 @@
import { App, Instruction, TAbstractFile, TFile } from "obsidian";
import { App, Instruction, Notice, TAbstractFile, TFile } from "obsidian";
import { PLUGIN_ID, VIEW_TYPE_EXCALIDRAW } from "./constants";
import ExcalidrawView from "./ExcalidrawView";
import { t } from "./lang/helpers";
import ExcalidrawPlugin from "./main";
import { GenericInputPrompt, GenericSuggester } from "./Prompt";
import { splitFolderAndFilename } from "./Utils";
import { errorlog, splitFolderAndFilename } from "./Utils";
export class ScriptEngine {
private plugin: ExcalidrawPlugin;
@@ -70,20 +71,23 @@ export class ScriptEngine {
this.loadScripts();
}
loadScripts() {
public getListofScripts(): TFile[] {
const app = this.plugin.app;
this.scriptPath = this.plugin.settings.scriptFolderPath;
if (!app.vault.getAbstractFileByPath(this.scriptPath)) {
this.scriptPath = null;
return;
}
const scripts = app.vault
return app.vault
.getFiles()
.filter((f: TFile) => f.path.startsWith(this.scriptPath));
scripts.forEach((f) => this.loadScript(f));
}
getScriptName(f: TFile | string): string {
loadScripts() {
this.getListofScripts()?.forEach((f) => this.loadScript(f));
}
public getScriptName(f: TFile | string): string {
let basename = "";
let path = "";
if (f instanceof TFile) {
@@ -161,9 +165,9 @@ 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,
{
let result = null;
try {
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: (
@@ -179,8 +183,11 @@ export class ScriptEngine {
hint,
instructions,
),
},
);
});
} catch (e) {
new Notice(t("SCRIPT_EXECUTION_ERROR"), 4000);
errorlog({ script: this.plugin.ea.activeScript, error: e });
}
this.plugin.ea.activeScript = null;
return result;
}

515
src/SuggestorInfo.ts Normal file
View File

@@ -0,0 +1,515 @@
import { FRONTMATTER_KEY, FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS, FRONTMATTER_KEY_CUSTOM_PREFIX, FRONTMATTER_KEY_CUSTOM_URL_PREFIX, FRONTMATTER_KEY_DEFAULT_MODE, FRONTMATTER_KEY_FONT, FRONTMATTER_KEY_FONTCOLOR, FRONTMATTER_KEY_MD_STYLE } from "./constants";
type SuggestorInfo = {
field: string,
code: string,
desc: string,
after: string
}
export const EXCALIDRAW_AUTOMATE_INFO:SuggestorInfo[] = [
{
field: "plugin",
code: null,
desc: "The ExcalidrawPlugin object",
after: "",
},
{
field: "elementsDict",
code: null,
desc: "The {} dictionary object, contains the ExcalidrawElements currently edited in Automate indexed by el.id",
after: '[""]',
},
{
field: "imagesDict",
code: null,
desc: "the images files including DataURL, indexed by fileId",
after: '[""]',
},
{
field: "style.strokeColor",
code: "[string]",
desc: "A valid css color. See <a onclick='window.open(\"https://www.w3schools.com/colors/default.asp\")'>W3 School Colors</a> for more.",
after: "",
},
{
field: "style.backgroundColor",
code: "[string]",
desc: "A valid css color. See <a onclick='window.open(\"https://www.w3schools.com/colors/default.asp\")'>W3 School Colors</a> for more.",
after: "",
},
{
field: "style.angle",
code: "[number]",
desc: "Rotation of the object in radian",
after: "",
},
{
field: "style.fillStyle",
code: "[string]",
desc: "'hachure' | 'cross-hatch' | 'solid'",
after: "",
},
{
field: "style.strokeWidth",
code: "[number]",
desc: null,
after: "",
},
{
field: "style.strokeStyle",
code: "[string]",
desc: "'solid' | 'dashed' | 'dotted'",
after: "",
},
{
field: "style.roughness",
code: "[number]",
desc: "0:Architect\n1:Artist\n2:Cartoonist",
after: "",
},
{
field: "style.opacity",
code: "[number]",
desc: "100: Fully opaque\n0: Fully transparent",
after: "",
},
{
field: "style.strokeSharpness",
code: "[string]",
desc: "'round' | 'sharp'",
after: "",
},
{
field: "style.fontFamily",
code: "[number]",
desc: "1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont",
after: "",
},
{
field: "style.fontSize",
code: "[number]",
desc: null,
after: "",
},
{
field: "style.textAlign",
code: "[string]",
desc: "'left' | 'right' | 'center'",
after: "",
},
{
field: "style.verticalAlign",
code: "[string]",
desc: "For future use, has no effect currently; 'top' | 'bottom' | 'middle'",
after: "",
},
{
field: "style.startArrowHead",
code: "[string]",
desc: "'triangle' | 'dot' | 'arrow' | 'bar' | null",
after: "",
},
{
field: "style.endArrowHead",
code: "[string]",
desc: "'triangle' | 'dot' | 'arrow' | 'bar' | null",
after: "",
},
{
field: "canvas.theme",
code: "[string]",
desc: "'dark' | 'light'",
after: "",
},
{
field: "canvas.viewBackgroundColor",
code: "[string]",
desc: "A valid css color.\nSee <a onclick='window.open(\"https://www.w3schools.com/colors/default.asp\")'>W3 School Colors</a> for more.",
after: "",
},
{
field: "canvas.gridSize",
code: "[number]",
desc: null,
after: "",
},
{
field: "addToGroup",
code: "addToGroup(objectIds: []): string;",
desc: null,
after: "",
},
{
field: "toCliboard",
code: "toClipboard(templatePath?: string): void;",
desc: "Copies current elements using template to clipboard, ready to be pasted into an excalidraw canvas",
after: "",
},
{
field: "getElements",
code: "getElements(): ExcalidrawElement[];",
desc: "Get all elements from ExcalidrawAutomate elementsDict",
after: "",
},
{
field: "getElement",
code: "getElement(id: string): ExcalidrawElement;",
desc: "Get single element from ExcalidrawAutomate elementsDict",
after: "",
},
{
field: "create",
code: 'create(params?: {filename?: string, foldername?: string, templatePath?: string, onNewPane?: boolean, frontmatterKeys?: { "excalidraw-plugin"?: "raw" | "parsed", "excalidraw-link-prefix"?: string, "excalidraw-link-brackets"?: boolean, "excalidraw-url-prefix"?: string,},}): Promise<string>;',
desc: 'Create a drawing and save it to filename.\nIf filename is null: default filename as defined in Excalidraw settings.\nIf folder is null: default folder as defined in Excalidraw settings\n',
after: "",
},
{
field: "createSVG",
code: "createSVG(templatePath?: string, embedFont?: boolean, exportSettings?: ExportSettings, loader?: EmbeddedFilesLoader, theme?: string,): Promise<SVGSVGElement>;",
desc: "Use ExcalidrawAutomate.getExportSettings(boolean,boolean) to create an ExportSettings object.\nUse ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?) to create an EmbeddedFilesLoader object.",
after: "",
},
{
field: "createPNG",
code: "createPNG(templatePath?: string, scale?: number, exportSettings?: ExportSettings, loader?: EmbeddedFilesLoader, theme?: string,): Promise<any>;",
desc: "Use ExcalidrawAutomate.getExportSettings(boolean,boolean) to create an ExportSettings object.\nUse ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?) to create an EmbeddedFilesLoader object.",
after: "",
},
{
field: "wrapText",
code: "wrapText(text: string, lineLen: number): string;",
desc: null,
after: "",
},
{
field: "addRect",
code: "addRect(topX: number, topY: number, width: number, height: number): string;",
desc: null,
after: "",
},
{
field: "addDiamond",
code: "addDiamond(topX: number, topY: number, width: number, height: number): string;",
desc: null,
after: "",
},
{
field: "addEllipse",
code: "addEllipse(topX: number, topY: number, width: number, height: number): string;",
desc: null,
after: "",
},
{
field: "addBlob",
code: "addBlob(topX: number, topY: number, width: number, height: number): string;",
desc: null,
after: "",
},
{
field: "addText",
code: 'addText(topX: number, topY: number, text: string, formatting?: {wrapAt?: number; width?: number; height?: number; textAlign?: string; box?: boolean | "box" | "blob" | "ellipse" | "diamond"; boxPadding?: number;}, id?: string,): string;',
desc: 'If box is !null, then text will be boxed\nThe function returns the id of the TextElement. If the text element is boxed i.e. it is a sticky note, then the id of the container object',
after: "",
},
{
field: "addLine",
code: "addLine(points: [[x: number, y: number]]): string;",
desc: null,
after: "",
},
{
field: "addArrow",
code: "addArrow(points: [[x: number, y: number]], formatting?: { startArrowHead?: string; endArrowHead?: string; startObjectId?: string; endObjectId?: string;},): string;",
desc: null,
after: "",
},
{
field: "addImage",
code: "addImage(topX: number, topY: number, imageFile: TFile): Promise<string>;",
desc: null,
after: "",
},
{
field: "addLaTex",
code: "addLaTex(topX: number, topY: number, tex: string): Promise<string>;",
desc: null,
after: "",
},
{
field: "connectObjects",
code: 'connectObjects(objectA: string, connectionA: ConnectionPoint, objectB: string, connectionB: ConnectionPoint, formatting?: {numberOfPoints?: number; startArrowHead?: string; endArrowHead?: string; padding?: number;},): void;',
desc: 'type ConnectionPoint = "top" | "bottom" | "left" | "right" | null\nWhen null is passed as ConnectionPoint then Excalidraw will automatically decide\nnumberOfPoints is the number of points on the line. Default is 0 i.e. line will only have a start and end point.\nArrowHead: "triangle"|"dot"|"arrow"|"bar"|null',
after: "",
},
{
field: "clear",
code: "clear(): void;",
desc: "Clears elementsDict and imagesDict only",
after: "",
},
{
field: "reset",
code: "reset(): void;",
desc: "clear() + reset all style values to default",
after: "",
},
{
field: "isExcalidrawFile",
code: "isExcalidrawFile(f: TFile): boolean;",
desc: "Returns true if MD file is an Excalidraw file",
after: "",
},
{
field: "targetView",
code: "targetView: ExcalidrawView;",
desc: "The Obsidian view currently edited",
after: "",
},
{
field: "setView",
code: 'setView(view: ExcalidrawView | "first" | "active"): ExcalidrawView;',
desc: null,
after: "",
},
{
field: "getExcalidrawAPI",
code: "getExcalidrawAPI(): any;",
desc: "<a onclick='window.open(\"https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw#ref\")'>Excalidraw API</a>",
after: "",
},
{
field: "getViewElements",
code: "getViewElements(): ExcalidrawElement[];",
desc: "Get elements in View",
after: "",
},
{
field: "deleteViewElements",
code: "deleteViewElements(el: ExcalidrawElement[]): boolean;",
desc: null,
after: "",
},
{
field: "getViewSelectedElement",
code: "getViewSelectedElement(): ExcalidrawElement;",
desc: "Get the selected element in the view, if more are selected, get the first",
after: "",
},
{
field: "getViewSelectedElements",
code: "getViewSelectedElements(): ExcalidrawElement[];",
desc: null,
after: "",
},
{
field: "getViewFileForImageElement",
code: "getViewFileForImageElement(el: ExcalidrawElement): TFile | null;",
desc: "Returns the TFile file handle for the image element",
after: "",
},
{
field: "copyViewElementsToEAforEditing",
code: "copyViewElementsToEAforEditing(elements: ExcalidrawElement[]): void;",
desc: "Copies elements from view to elementsDict for editing",
after: "",
},
{
field: "viewToggleFullScreen",
code: "viewToggleFullScreen(forceViewMode?: boolean): void;",
desc: null,
after: "",
},
{
field: "connectObjectWithViewSelectedElement",
code: "connectObjectWithViewSelectedElement(objectA: string, connectionA: ConnectionPoint, connectionB: ConnectionPoint, formatting?: {numberOfPoints?: number; startArrowHead?: string; endArrowHead?: string; padding?: number;},): boolean;",
desc: "Connect an object to the selected element in the view\nSee tooltip for connectObjects for details",
after: "",
},
{
field: "addElementsToView",
code: "addElementsToView(repositionToCursor?: boolean, save?: boolean, newElementsOnTop?: boolean,): Promise<boolean>;",
desc: "Adds elements from elementsDict to the current view\nrepositionToCursor: default is false\nsave: default is true\nnewElementsOnTop: default is false, i.e. the new elements get to the bottom of the stack\nnewElementsOnTop controls whether elements created with ExcalidrawAutomate are added at the bottom of the stack or the top of the stack of elements already in the view\nNote that elements copied to the view with copyViewElementsToEAforEditing retain their position in the stack of elements in the view even if modified using EA",
after: "",
},
{
field: "onDropHook",
code: 'onDropHook(data: {ea: ExcalidrawAutomate, event: React.DragEvent<HTMLDivElement>, draggable: any, type: "file" | "text" | "unknown", payload: {files: TFile[], text: string,}, excalidrawFile: TFile, view: ExcalidrawView, pointerPosition: { x: number, y: number},}): boolean;',
desc: 'If set Excalidraw will call this function onDrop events.\nA return of true will stop the default onDrop processing in Excalidraw.\n\ndraggable is the Obsidian draggable object\nfiles is the array of dropped files\nexcalidrawFile is the file receiving the drop event\nview is the excalidraw view receiving the drop.\npointerPosition is the pointer position on canvas at the time of drop.',
after: "",
},
{
field: "mostRecentMarkdownSVG",
code: "mostRecentMarkdownSVG: SVGSVGElement;",
desc: "Markdown renderer will drop a copy of the most recent SVG here for debugging purposes",
after: "",
},
{
field: "getEmbeddedFilesLoader",
code: "getEmbeddedFilesLoader(isDark?: boolean): EmbeddedFilesLoader;",
desc: "Utility function to generate EmbeddedFilesLoader object",
after: "",
},
{
field: "getExportSettings",
code: "getExportSettings(withBackground: boolean, withTheme: boolean,): ExportSettings;",
desc: "Utility function to generate ExportSettings object",
after: "",
},
{
field: "getBoundingBox",
code: "getBoundingBox(elements: ExcalidrawElement[]): {topX: number, topY: number, width: number, height: number,};",
desc: "Gets the bounding box of elements. The bounding box is the box encapsulating all of the elements completely.",
after: "",
},
{
field: "getMaximumGroups",
code: "getMaximumGroups(elements: ExcalidrawElement[]): ExcalidrawElement[][];",
desc: "Elements grouped by the highest level groups",
after: "",
},
{
field: "getLargestElement",
code: "getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;",
desc: "Gets the largest element from a group. useful when a text element is grouped with a box, and you want to connect an arrow to the box",
after: "",
},
{
field: "intersectElementWithLine",
code: "intersectElementWithLine(element: ExcalidrawBindableElement, a: readonly [number, number], b: readonly [number, number], gap?: number,): Point[];",
desc: "If gap is given, the element is inflated by this value.\nReturns 2 or 0 intersection points between line going through `a` and `b` and the `element`, in ascending order of distance from `a`.",
after: "",
},
{
field: "getLargestElement",
code: "getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;",
desc: "Gets the largest element from a group. useful when a text element is grouped with a box, and you want to connect an arrow to the box",
after: "",
},
{
field: "activeScript",
code: "activeScript: string;",
desc: "Mandatory to set before calling the get and set ScriptSettings functions. Set automatically by the ScriptEngine\nSee for more details: <a onclick='window.open(\"https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html\")'>Script Engine Help</a>",
after: "",
},
{
field: "getScriptSettings",
code: "getScriptSettings(): {};",
desc: "Returns script settings. Saves settings in plugin settings, under the activeScript key. See for more details: <a onclick='window.open(\"https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html\")'>Script Engine Help</a>",
after: "",
},
{
field: "setScriptSettings",
code: "setScriptSettings(settings: any): Promise<void>;",
desc: "Sets script settings.\nSee for more details: <a onclick='window.open(\"https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html\")'>Script Engine Help</a>",
after: "",
},
{
field: "openFileInNewOrAdjacentLeaf",
code: "openFileInNewOrAdjacentLeaf(file: TFile): WorkspaceLeaf;",
desc: "Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings",
after: "",
},
{
field: "measureText",
code: "measureText(text: string): { width: number; height: number };",
desc: "Measures text size based on current style settings",
after: "",
},
{
field: "verifyMinimumPluginVersion",
code: 'verifyMinimumPluginVersion(requiredVersion: string): boolean;',
desc: 'Returns true if plugin version is >= than required\nrecommended use:\n<code>if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.20")) {new Notice("message");return;}<code>',
after: "",
},
{
field: "selectElementsInView",
code: "selectElementsInView(elements: ExcalidrawElement[]):void;",
desc: "Elements provided will be set as selected in the targetView.",
after: "",
},
{
field: "generateElementId",
code: "generateElementId(): string;",
desc: "Returns an 8 character long random id",
after: "",
},
{
field: "cloneElement",
code: "cloneElement(element: ExcalidrawElement): ExcalidrawElement;",
desc: "Returns a clone of the element with a new element id",
after: "",
},
{
field: "moveViewElementToZIndex",
code: "moveViewElementToZIndex(elementId:number, newZIndex:number): void;",
desc: "Moves the element to a specific position in the z-index",
after: "",
},
];
export const EXCALIDRAW_SCRIPTENGINE_INFO:SuggestorInfo[] = [
{
field: "inputPrompt",
code: "inputPrompt: (header: string, placeholder?: string, value?: string);",
desc: "Opens a prompt that asks for an input.\nReturns a string with the input.\nYou need to await the result of inputPrompt.",
after: "",
},
{
field: "suggester",
code: "suggester: (displayItems: string[], items: any[], hint?: string, instructions?:Instruction[]);",
desc: "Opens a suggester. Displays the displayItems and returns the corresponding item from items[]\nYou need to await the result of suggester.\nIf the user cancels (ESC), suggester will return undefined\nHint and instructions are optional\n\n<code>interface Instruction {command: string;purpose: string;}</code>",
after: "",
},
];
export const FRONTMATTER_KEYS_INFO:SuggestorInfo[] = [
{
field: FRONTMATTER_KEY,
code: null,
desc: "Denotes an excalidraw file. If key is not present, the file will not be recognized as an Excalidarw file. Valid values are 'parsed' and 'raw'",
after: ": parsed",
},
{
field: FRONTMATTER_KEY_CUSTOM_PREFIX,
code: null,
desc: "Set custom prefix to denote text element containing a valid internal link. Set to empty string if you do not want to show a prefix",
after: ': "📍"',
},
{
field: FRONTMATTER_KEY_CUSTOM_URL_PREFIX,
code: null,
desc: "Set custom prefix to denote text element containing a valid external link. Set to empty string if you do not want to show a prefix",
after: ': "🌐"',
},
{
field: FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS,
code: null,
desc: "Set to true, if you want to display [[square brackets]] around the links in Text Elements",
after: ": true",
},
{
field: FRONTMATTER_KEY_DEFAULT_MODE,
code: null,
desc: "Specifies how Excalidraw should open by default. Valid values are: view|zen",
after: ": view",
},
{
field: FRONTMATTER_KEY_FONT,
code: null,
desc: "This key applies to Markdown Embeds. You can control the appearance of the embedded markdown file on a file by file bases by adding the this front matter keys to your markdown document. Valid values are: Virgil|Cascadia|font_file_name.extension",
after: ": Virgil",
},
{
field: FRONTMATTER_KEY_FONTCOLOR,
code: null,
desc: "This key applies to Markdown Embeds. You can control the appearance of the embedded markdown file on a file by file bases by adding the this front matter keys to your markdown document. Valid values are: css-color-name|#HEXcolor|any-other-html-standard-format",
after: ": SteelBlue",
},
{
field: FRONTMATTER_KEY_MD_STYLE,
code: null,
desc: 'This key applies to Markdown Embeds. You can control the appearance of the embedded markdown file on a file by file bases by adding the this front matter keys to your markdown document. Valid values are: "css-filename|css snippet"',
after: ': ""',
},
];

View File

@@ -277,8 +277,9 @@ export const viewportCoordsToSceneCoords = (
},
) => {
const invScale = 1 / zoom.value;
const x = (clientX - zoom.translation.x - offsetLeft) * invScale - scrollX;
const y = (clientY - zoom.translation.y - offsetTop) * invScale - scrollY;
const x = (clientX - offsetLeft) * invScale - scrollX;
const y = (clientY - offsetTop) * invScale - scrollY;
return { x, y };
};
@@ -404,6 +405,7 @@ export const getAttachmentsFolderAndFilePath = async (
export const getSVG = async (
scene: any,
exportSettings: ExportSettings,
padding: number,
): Promise<SVGSVGElement> => {
try {
return await exportToSvg({
@@ -416,7 +418,7 @@ export const getSVG = async (
...scene.appState,
},
files: scene.files,
exportPadding: 10,
exportPadding: padding,
});
} catch (error) {
return null;

View File

@@ -29,6 +29,7 @@ export const FRONTMATTER_KEY_DEFAULT_MODE = "excalidraw-default-mode";
export const FRONTMATTER_KEY_FONT = "excalidraw-font";
export const FRONTMATTER_KEY_FONTCOLOR = "excalidraw-font-color";
export const FRONTMATTER_KEY_MD_STYLE = "excalidraw-css";
export const VIEW_TYPE_EXCALIDRAW = "excalidraw";
export const ICON_NAME = "excalidraw-icon";
export const MAX_COLORS = 5;

View File

@@ -57,7 +57,7 @@ export default {
"SHIFT CLICK this button to open the link in a new pane.\n" +
"CTRL/CMD CLICK the Image or TextElement on the canvas has the same effect!",
TEXT_ELEMENT_EMPTY:
"No ImageElement is selected or TextElement is empty, or [[valid-link|alias]] or [alias](valid-link) is not found",
"No ImageElement is selected or TextElement is empty, or [[valid-link|alias]]</code> or <code>[alias](valid-link) is not found",
FILENAME_INVALID_CHARS:
'File name cannot contain any of the following characters: * " \\ < > : | ?',
FILE_DOES_NOT_EXIST:
@@ -144,33 +144,33 @@ export default {
"Set the maximum level to which zoom to fit will enlarge the drawing. Minimum is 0.5 (50%) and maximum is 10 (1000%).",
LINKS_HEAD: "Links and transclusion",
LINKS_DESC:
"CTRL/CMD + CLICK on [[Text Elements]] to open them as links. " +
"If the selected text has more than one [[valid Obsidian links]], only the first will be opened. " +
"If the text starts as a valid web link (i.e. https:// or http://), then " +
"CTRL/CMD + CLICK on <code>[[Text Elements]]</code> to open them as links. " +
"If the selected text has more than one <code>[[valid Obsidian links]]</code>, only the first will be opened. " +
"If the text starts as a valid web link (i.e. <code>https://</code> or <code>http://</code>), then " +
"the plugin will open it in a browser. " +
"When Obsidian files change, the matching [[link]] in your drawings will also change. " +
"If you don't want text accidentally changing in your drawings use [[links|with aliases]].",
"When Obsidian files change, the matching <code>[[link]]</code> in your drawings will also change. " +
"If you don't want text accidentally changing in your drawings use <code>[[links|with aliases]]</code>.",
ADJACENT_PANE_NAME: "Open in adjacent pane",
ADJACENT_PANE_DESC:
"When CTRL/CMD+SHIFT clicking a link in Excalidraw by default the plugin will open the link in a new pane. " +
"Turning this setting on, Excalidraw will first look for an existing adjacent pane, and try to open the link there. " +
"Excalidraw will first look too the right, then to the left, then down, then up. If no pane is found, Excalidraw will open " +
"a new pane.",
LINK_BRACKETS_NAME: "Show [[brackets]] around links",
LINK_BRACKETS_NAME: "Show <code>[[brackets]]</code> around links",
LINK_BRACKETS_DESC: `${
"In PREVIEW mode, when parsing Text Elements, place brackets around links. " +
"You can override this setting for a specific drawing by adding '"
}${FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS}: true/false' to the file's frontmatter.`,
"You can override this setting for a specific drawing by adding <code>"
}${FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS}: true/false</code> to the file's frontmatter.`,
LINK_PREFIX_NAME: "Link prefix",
LINK_PREFIX_DESC: `${
"In PREVIEW mode, if the Text Element contains a link, precede the text with these characters. " +
"You can override this setting for a specific drawing by adding '"
}${FRONTMATTER_KEY_CUSTOM_PREFIX}: "📍 "' to the file's frontmatter.`,
"You can override this setting for a specific drawing by adding <code>"
}${FRONTMATTER_KEY_CUSTOM_PREFIX}: "📍 "</code> to the file's frontmatter.`,
URL_PREFIX_NAME: "URL prefix",
URL_PREFIX_DESC: `${
"In PREVIEW mode, if the Text Element contains a URL link, precede the text with these characters. " +
"You can override this setting for a specific drawing by adding '"
}${FRONTMATTER_KEY_CUSTOM_URL_PREFIX}: "🌐 "' to the file's frontmatter.`,
"You can override this setting for a specific drawing by adding <code>"
}${FRONTMATTER_KEY_CUSTOM_URL_PREFIX}: "🌐 "</code> to the file's frontmatter.`,
LINK_CTRL_CLICK_NAME:
"CTRL/CMD + CLICK on text with [[links]] or [](links) to open them",
LINK_CTRL_CLICK_DESC:
@@ -187,7 +187,7 @@ export default {
"![[markdown page]] format.",
GET_URL_TITLE_NAME: "Use iframely to resolve page title",
GET_URL_TITLE_DESC:
"Use the http://iframely.server.crestify.com/iframely?url= to get title of page when dropping a link into Excalidraw",
"Use the <code>http://iframely.server.crestify.com/iframely?url=</code> to get title of page when dropping a link into Excalidraw",
MD_HEAD: "Markdown-embed settings",
MD_HEAD_DESC:
"You can transclude formatted markdown documents into drawings as images CTRL/CMD drop from the file explorer or using " +
@@ -196,17 +196,17 @@ export default {
MD_TRANSCLUDE_WIDTH_DESC:
"The width of the markdown page. This effects the word wrapping when transcluding longer paragraphs, and the width of " +
"the image element. You can override the default width of " +
"an embedded file using the [[filename#heading|WIDTHxMAXHEIGHT]] syntax in markdown view mode under embedded files.",
"an embedded file using the <code>[[filename#heading|WIDTHxMAXHEIGHT]]</code> syntax in markdown view mode under embedded files.",
MD_TRANSCLUDE_HEIGHT_NAME:
"Default maximum height of a transcluded markdown document",
MD_TRANSCLUDE_HEIGHT_DESC:
"The embedded image will be as high as the markdown text requries, but not higher than this value. " +
"You can override this value by editing the embedded image link in markdown view mode with the following syntax [[filename#^blockref|WIDTHxMAXHEIGHT]].",
"You can override this value by editing the embedded image link in markdown view mode with the following syntax <code>[[filename#^blockref|WIDTHxMAXHEIGHT]]</code>.",
MD_DEFAULT_FONT_NAME:
"The default font typeface to use for embedded markdown files.",
MD_DEFAULT_FONT_DESC:
'Set this value to "Virgil" or "Cascadia" or the filename of a valid .ttf, .woff, or .woff2 font e.g. "MyFont.woff2" ' +
'You can override this setting by adding the following frontmatter-key to the embedded markdown file: "excalidraw-font: font_or_filename"',
'Set this value to "Virgil" or "Cascadia" or the filename of a valid <code>.ttf</code>, <code>.woff</code>, or <code>.woff2</code> font e.g. <code>MyFont.woff2</code> ' +
"You can override this setting by adding the following frontmatter-key to the embedded markdown file: <code>excalidraw-font: font_or_filename</code>",
MD_DEFAULT_COLOR_NAME:
"The default font color to use for embedded markdown files.",
MD_DEFAULT_COLOR_DESC:
@@ -232,8 +232,8 @@ export default {
EMBED_WIDTH_NAME: "Default width of embedded (transcluded) image",
EMBED_WIDTH_DESC:
"Only relevant if embed type is excalidraw. Has no effect on PNG and SVG embeds. The default width of an embedded drawing. You can specify a custom " +
"width when embedding an image using the ![[drawing.excalidraw|100]] or " +
"[[drawing.excalidraw|100x100]] format.",
"width when embedding an image using the <code>![[drawing.excalidraw|100]]</code> or " +
"<code>[[drawing.excalidraw|100x100]]</code> format.",
EMBED_TYPE_NAME: "Type of file to insert into the document",
EMBED_TYPE_DESC:
"When you embed an image into a document using the command palette this setting will specify if Excalidraw should embed the original excalidraw file " +
@@ -245,6 +245,8 @@ export default {
EXPORT_BACKGROUND_NAME: "Export image with background",
EXPORT_BACKGROUND_DESC:
"If turned off, the exported image will be transparent.",
EXPORT_SVG_PADDING_NAME: "SVG Padding",
EXPORT_SVG_PADDING_DESC: "The padding (in pixels) around the exported SVG image. If you have curved lines close to the edge of the image they might get cropped during SVG export. You can increase this value to avoid cropping.",
EXPORT_THEME_NAME: "Export image with theme",
EXPORT_THEME_DESC:
"Export the image matching the dark/light theme of your drawing. If turned off, " +
@@ -278,7 +280,11 @@ export default {
"when you open a legacy file for editing.",
EXPERIMENTAL_HEAD: "Experimental features",
EXPERIMENTAL_DESC:
"These setting will not take effect immediately, only when the File Explorer is refreshed, or Obsidian restarted.",
"Some of these setting will not take effect immediately, only when the File Explorer is refreshed, or Obsidian restarted.",
FIELD_SUGGESTOR_NAME: "Enable Field Suggestor",
FIELD_SUGGESTOR_DESC:
"Field Suggestor borrowed from Breadcrumbs and Templater plugins. The Field Suggestor will show an autocomplete menu " +
"when you type <code>excalidraw-</code> or <code>ea.</code> with function description as hints on the individual items in the list.",
FILETYPE_NAME: "Display type (✏️) for excalidraw.md files in File Explorer",
FILETYPE_DESC:
"Excalidraw files will receive an indicator using the emojii or text defined in the next setting.",
@@ -316,4 +322,11 @@ export default {
//EmbeddedFileLoader.ts
INFINITE_LOOP_WARNING:
"EXCALIDRAW WARNING\nAborted loading embedded images due to infinite loop in file:\n",
//Scripts.ts
SCRIPT_EXECUTION_ERROR:
"Script execution error. Please find error message on the developer console.",
//ExcalidrawData.ts
LOAD_FROM_BACKUP: "Excalidraw file was corrupted. Loading from backup file.",
};

View File

@@ -70,6 +70,7 @@ import {
getNewUniqueFilepath,
isObsidianThemeDark,
log,
sleep,
} from "./Utils";
import { OneOffs } from "./OneOffs";
import { FileId } from "@zsviczian/excalidraw/types/element/types";
@@ -80,6 +81,7 @@ import {
markdownPostProcessor,
observer,
} from "./MarkdownPostProcessor";
import { FieldSuggestor } from "./FieldSuggestor";
declare module "obsidian" {
interface App {
@@ -156,6 +158,7 @@ export default class ExcalidrawPlugin extends Plugin {
this.registerCommands();
this.registerEventListeners();
this.initializeFourthFont();
this.registerEditorSuggest(new FieldSuggestor(this));
//inspiration taken from kanban:
//https://github.com/mgmeyers/obsidian-kanban/blob/44118e25661bff9ebfe54f71ae33805dc88ffa53/src/main.ts#L267
@@ -659,7 +662,9 @@ export default class ExcalidrawPlugin extends Plugin {
this.lastActiveExcalidrawFilePath != null
);
}
this.embedDrawing(this.lastActiveExcalidrawFilePath);
const file = this.app.vault.getAbstractFileByPath(this.lastActiveExcalidrawFilePath);
if(!(file instanceof TFile)) return false;
this.embedDrawing(file);
return true;
},
});
@@ -705,7 +710,7 @@ export default class ExcalidrawPlugin extends Plugin {
)
).folder;
const file = await this.createDrawing(filename, folder);
await this.embedDrawing(file.path);
await this.embedDrawing(file);
this.openDrawing(file, inNewPane);
};
@@ -1247,16 +1252,19 @@ export default class ExcalidrawPlugin extends Plugin {
//delete PNG and SVG files as well
if (self.settings.keepInSync) {
[".svg", ".png", ".excalidraw"].forEach(async (ext: string) => {
const imgPath = getIMGPathFromExcalidrawFile(file.path, ext);
const imgFile = self.app.vault.getAbstractFileByPath(
normalizePath(imgPath),
);
if (imgFile && imgFile instanceof TFile) {
await self.app.vault.delete(imgFile);
}
});
setTimeout(()=>{
[".svg", ".png", ".excalidraw"].forEach(async (ext: string) => {
const imgPath = getIMGPathFromExcalidrawFile(file.path, ext);
const imgFile = self.app.vault.getAbstractFileByPath(
normalizePath(imgPath),
);
if (imgFile && imgFile instanceof TFile) {
await self.app.vault.delete(imgFile);
}
});
},500);
}
};
self.registerEvent(self.app.vault.on("delete", deleteEventHandler));
@@ -1368,20 +1376,31 @@ export default class ExcalidrawPlugin extends Plugin {
//this.saveSettings();
}
public async embedDrawing(data: string) {
public async embedDrawing(file: TFile) {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (activeView) {
if (activeView && activeView.file) {
const data = this.app.metadataCache.fileToLinktext(
file,
activeView.file.path,
this.settings.embedType === "excalidraw"
)
const editor = activeView.editor;
if (this.settings.embedType === "excalidraw") {
editor.replaceSelection(`![[${data}]]`);
editor.focus();
return;
}
const filename = `${data.substring(
0,
data.lastIndexOf("."),
)}.${this.settings.embedType.toLowerCase()}`;
await this.app.vault.create(filename, "");
const filename = getIMGPathFromExcalidrawFile(
data,"."+this.settings.embedType.toLowerCase()
);
const filepath = getIMGPathFromExcalidrawFile(
file.path,"."+this.settings.embedType.toLowerCase()
);
await this.app.vault.create(filepath, "");
//await sleep(200);
editor.replaceSelection(
`![[${filename}]]\n%%[[${data}|🖋 Edit in Excalidraw]]%%`,
);
@@ -1457,7 +1476,7 @@ export default class ExcalidrawPlugin extends Plugin {
);
}
private async getBlankDrawing(): Promise<string> {
public async getBlankDrawing(): Promise<string> {
const template = this.app.metadataCache.getFirstLinkpathDest(
normalizePath(this.settings.templateFilePath),
"",

View File

@@ -58,7 +58,7 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
this.plugin.openDrawing(item, this.onNewPane);
break;
case openDialogAction.insertLinkToDrawing:
this.plugin.embedDrawing(item.path);
this.plugin.embedDrawing(item);
break;
}
}

View File

@@ -39,6 +39,7 @@ export interface ExcalidrawSettings {
pngExportScale: number;
exportWithTheme: boolean;
exportWithBackground: boolean;
exportPaddingSVG: number;
keepInSync: boolean;
autoexportSVG: boolean;
autoexportPNG: boolean;
@@ -51,6 +52,7 @@ export interface ExcalidrawSettings {
experimentalLivePreview: boolean;
experimentalEnableFourthFont: boolean;
experimantalFourthFont: string;
fieldSuggestor: boolean;
loadCount: number; //version 1.2 migration counter
drawingOpenCount: number;
library: string;
@@ -95,6 +97,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
pngExportScale: 1,
exportWithTheme: true,
exportWithBackground: true,
exportPaddingSVG: 10,
keepInSync: false,
autoexportSVG: false,
autoexportPNG: false,
@@ -106,6 +109,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
experimentalLivePreview: true,
experimentalEnableFourthFont: false,
experimantalFourthFont: "Virgil",
fieldSuggestor: true,
compatibilityMode: false,
loadCount: 0,
drawingOpenCount: 0,
@@ -128,6 +132,9 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
scriptEngineSettings: {},
};
const fragWithHTML = (html: string) =>
createFragment((frag) => (frag.createDiv().innerHTML = html));
export class ExcalidrawSettingTab extends PluginSettingTab {
plugin: ExcalidrawPlugin;
private requestEmbedUpdate: boolean = false;
@@ -195,7 +202,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("FOLDER_NAME"))
.setDesc(t("FOLDER_DESC"))
.setDesc(fragWithHTML(t("FOLDER_DESC")))
.addText((text) =>
text
.setPlaceholder("Excalidraw")
@@ -208,7 +215,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("FOLDER_EMBED_NAME"))
.setDesc(t("FOLDER_EMBED_DESC"))
.setDesc(fragWithHTML(t("FOLDER_EMBED_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.embedUseExcalidrawFolder)
@@ -220,7 +227,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("TEMPLATE_NAME"))
.setDesc(t("TEMPLATE_DESC"))
.setDesc(fragWithHTML(t("TEMPLATE_DESC")))
.addText((text) =>
text
.setPlaceholder("Excalidraw/Template")
@@ -233,7 +240,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("SCRIPT_FOLDER_NAME"))
.setDesc(t("SCRIPT_FOLDER_DESC"))
.setDesc(fragWithHTML(t("SCRIPT_FOLDER_DESC")))
.addText((text) =>
text
.setPlaceholder("Excalidraw/Scripts")
@@ -262,7 +269,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("FILENAME_PREFIX_NAME"))
.setDesc(t("FILENAME_PREFIX_DESC"))
.setDesc(fragWithHTML(t("FILENAME_PREFIX_DESC")))
.addText((text) =>
text
.setPlaceholder("Drawing ")
@@ -280,7 +287,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("FILENAME_PREFIX_EMBED_NAME"))
.setDesc(t("FILENAME_PREFIX_EMBED_DESC"))
.setDesc(fragWithHTML(t("FILENAME_PREFIX_EMBED_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.drawingEmbedPrefixWithFilename)
@@ -292,7 +299,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("FILENAME_DATE_NAME"))
.setDesc(t("FILENAME_DATE_DESC"))
.setDesc(fragWithHTML(t("FILENAME_DATE_DESC")))
.addText((text) =>
text
.setPlaceholder("YYYY-MM-DD HH.mm.ss")
@@ -312,7 +319,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("MATCH_THEME_NAME"))
.setDesc(t("MATCH_THEME_DESC"))
.setDesc(fragWithHTML(t("MATCH_THEME_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.matchTheme)
@@ -324,7 +331,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("MATCH_THEME_ALWAYS_NAME"))
.setDesc(t("MATCH_THEME_ALWAYS_DESC"))
.setDesc(fragWithHTML(t("MATCH_THEME_ALWAYS_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.matchThemeAlways)
@@ -336,7 +343,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("MATCH_THEME_TRIGGER_NAME"))
.setDesc(t("MATCH_THEME_TRIGGER_DESC"))
.setDesc(fragWithHTML(t("MATCH_THEME_TRIGGER_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.matchThemeTrigger)
@@ -348,7 +355,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("DEFAULT_OPEN_MODE_NAME"))
.setDesc(t("DEFAULT_OPEN_MODE_DESC"))
.setDesc(fragWithHTML(t("DEFAULT_OPEN_MODE_DESC")))
.addDropdown((dropdown) =>
dropdown
.addOption("normal", "Normal Mode")
@@ -363,7 +370,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("ZOOM_TO_FIT_NAME"))
.setDesc(t("ZOOM_TO_FIT_DESC"))
.setDesc(fragWithHTML(t("ZOOM_TO_FIT_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.zoomToFitOnResize)
@@ -377,7 +384,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("ZOOM_TO_FIT_MAX_LEVEL_NAME"))
.setDesc(t("ZOOM_TO_FIT_MAX_LEVEL_DESC"))
.setDesc(fragWithHTML(t("ZOOM_TO_FIT_MAX_LEVEL_DESC")))
.addSlider((slider) =>
slider
.setLimits(0.5, 10, 0.5)
@@ -396,11 +403,15 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
});
this.containerEl.createEl("h1", { text: t("LINKS_HEAD") });
this.containerEl.createEl("p", { text: t("LINKS_DESC") });
this.containerEl.createEl(
"span",
undefined,
(el) => (el.innerHTML = t("LINKS_DESC")),
);
new Setting(containerEl)
.setName(t("ADJACENT_PANE_NAME"))
.setDesc(t("ADJACENT_PANE_DESC"))
.setDesc(fragWithHTML(t("ADJACENT_PANE_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.openInAdjacentPane)
@@ -412,7 +423,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("LINK_BRACKETS_NAME"))
.setDesc(t("LINK_BRACKETS_DESC"))
.setDesc(fragWithHTML(t("LINK_BRACKETS_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.showLinkBrackets)
@@ -424,7 +435,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("LINK_PREFIX_NAME"))
.setDesc(t("LINK_PREFIX_DESC"))
.setDesc(fragWithHTML(t("LINK_PREFIX_DESC")))
.addText((text) =>
text
.setPlaceholder(t("INSERT_EMOJI"))
@@ -437,7 +448,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("URL_PREFIX_NAME"))
.setDesc(t("URL_PREFIX_DESC"))
.setDesc(fragWithHTML(t("URL_PREFIX_DESC")))
.addText((text) =>
text
.setPlaceholder(t("INSERT_EMOJI"))
@@ -450,7 +461,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("LINK_CTRL_CLICK_NAME"))
.setDesc(t("LINK_CTRL_CLICK_DESC"))
.setDesc(fragWithHTML(t("LINK_CTRL_CLICK_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.allowCtrlClick)
@@ -462,7 +473,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
const s = new Setting(containerEl)
.setName(t("TRANSCLUSION_WRAP_NAME"))
.setDesc(t("TRANSCLUSION_WRAP_DESC"))
.setDesc(fragWithHTML(t("TRANSCLUSION_WRAP_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.forceWrap)
@@ -477,7 +488,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("PAGE_TRANSCLUSION_CHARCOUNT_NAME"))
.setDesc(t("PAGE_TRANSCLUSION_CHARCOUNT_DESC"))
.setDesc(fragWithHTML(t("PAGE_TRANSCLUSION_CHARCOUNT_DESC")))
.addText((text) =>
text
.setPlaceholder("Enter a number")
@@ -506,7 +517,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("GET_URL_TITLE_NAME"))
.setDesc(t("GET_URL_TITLE_DESC"))
.setDesc(fragWithHTML(t("GET_URL_TITLE_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.iframelyAllowed)
@@ -521,7 +532,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("MD_TRANSCLUDE_WIDTH_NAME"))
.setDesc(t("MD_TRANSCLUDE_WIDTH_DESC"))
.setDesc(fragWithHTML(t("MD_TRANSCLUDE_WIDTH_DESC")))
.addText((text) =>
text
.setPlaceholder("Enter a number e.g. 500")
@@ -547,7 +558,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("MD_TRANSCLUDE_HEIGHT_NAME"))
.setDesc(t("MD_TRANSCLUDE_HEIGHT_DESC"))
.setDesc(fragWithHTML(t("MD_TRANSCLUDE_HEIGHT_DESC")))
.addText((text) =>
text
.setPlaceholder("Enter a number e.g. 800")
@@ -573,7 +584,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("MD_DEFAULT_FONT_NAME"))
.setDesc(t("MD_DEFAULT_FONT_DESC"))
.setDesc(fragWithHTML(t("MD_DEFAULT_FONT_DESC")))
.addDropdown(async (d: DropdownComponent) => {
d.addOption("Virgil", "Virgil");
d.addOption("Cascadia", "Cascadia");
@@ -592,7 +603,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("MD_DEFAULT_COLOR_NAME"))
.setDesc(t("MD_DEFAULT_COLOR_DESC"))
.setDesc(fragWithHTML(t("MD_DEFAULT_COLOR_DESC")))
.addText((text) =>
text
.setPlaceholder("CSS Color-name|RGB-HEX")
@@ -606,7 +617,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("MD_CSS_NAME"))
.setDesc(t("MD_CSS_DESC"))
.setDesc(fragWithHTML(t("MD_CSS_DESC")))
.addText((text) =>
text
.setPlaceholder("filename of css file in vault")
@@ -622,7 +633,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("EMBED_PREVIEW_SVG_NAME"))
.setDesc(t("EMBED_PREVIEW_SVG_DESC"))
.setDesc(fragWithHTML(t("EMBED_PREVIEW_SVG_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.displaySVGInPreview)
@@ -634,7 +645,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("PREVIEW_MATCH_OBSIDIAN_NAME"))
.setDesc(t("PREVIEW_MATCH_OBSIDIAN_DESC"))
.setDesc(fragWithHTML(t("PREVIEW_MATCH_OBSIDIAN_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.previewMatchObsidianTheme)
@@ -646,7 +657,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("EMBED_WIDTH_NAME"))
.setDesc(t("EMBED_WIDTH_DESC"))
.setDesc(fragWithHTML(t("EMBED_WIDTH_DESC")))
.addText((text) =>
text
.setPlaceholder("400")
@@ -662,7 +673,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("EMBED_TYPE_NAME"))
.setDesc(t("EMBED_TYPE_DESC"))
.setDesc(fragWithHTML(t("EMBED_TYPE_DESC")))
.addDropdown(async (d: DropdownComponent) => {
dropdown = d;
dropdown.addOption("excalidraw", "excalidraw");
@@ -691,7 +702,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("EXPORT_PNG_SCALE_NAME"))
.setDesc(t("EXPORT_PNG_SCALE_DESC"))
.setDesc(fragWithHTML(t("EXPORT_PNG_SCALE_DESC")))
.addSlider((slider) =>
slider
.setLimits(1, 5, 0.5)
@@ -711,7 +722,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("EXPORT_BACKGROUND_NAME"))
.setDesc(t("EXPORT_BACKGROUND_DESC"))
.setDesc(fragWithHTML(t("EXPORT_BACKGROUND_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.exportWithBackground)
@@ -722,9 +733,31 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}),
);
let exportPadding: HTMLDivElement;
new Setting(containerEl)
.setName(t("EXPORT_SVG_PADDING_NAME"))
.setDesc(fragWithHTML(t("EXPORT_SVG_PADDING_DESC")))
.addSlider((slider) =>
slider
.setLimits(0,50,5)
.setValue(this.plugin.settings.exportPaddingSVG)
.onChange(async (value) => {
exportPadding.innerText = ` ${value.toString()}`;
this.plugin.settings.exportPaddingSVG = value;
this.applySettingsUpdate();
}),
)
.settingEl.createDiv("", (el) => {
exportPadding = el;
el.style.minWidth = "2.3em";
el.style.textAlign = "right";
el.innerText = ` ${this.plugin.settings.exportPaddingSVG.toString()}`;
});
new Setting(containerEl)
.setName(t("EXPORT_THEME_NAME"))
.setDesc(t("EXPORT_THEME_DESC"))
.setDesc(fragWithHTML(t("EXPORT_THEME_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.exportWithTheme)
@@ -739,7 +772,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("EXPORT_SYNC_NAME"))
.setDesc(t("EXPORT_SYNC_DESC"))
.setDesc(fragWithHTML(t("EXPORT_SYNC_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.keepInSync)
@@ -760,7 +793,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("EXPORT_SVG_NAME"))
.setDesc(t("EXPORT_SVG_DESC"))
.setDesc(fragWithHTML(t("EXPORT_SVG_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.autoexportSVG)
@@ -781,7 +814,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("EXPORT_PNG_NAME"))
.setDesc(t("EXPORT_PNG_DESC"))
.setDesc(fragWithHTML(t("EXPORT_PNG_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.autoexportPNG)
@@ -804,7 +837,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("COMPATIBILITY_MODE_NAME"))
.setDesc(t("COMPATIBILITY_MODE_DESC"))
.setDesc(fragWithHTML(t("COMPATIBILITY_MODE_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.compatibilityMode)
@@ -816,7 +849,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("EXPORT_EXCALIDRAW_NAME"))
.setDesc(t("EXPORT_EXCALIDRAW_DESC"))
.setDesc(fragWithHTML(t("EXPORT_EXCALIDRAW_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.autoexportExcalidraw)
@@ -828,7 +861,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("SYNC_EXCALIDRAW_NAME"))
.setDesc(t("SYNC_EXCALIDRAW_DESC"))
.setDesc(fragWithHTML(t("SYNC_EXCALIDRAW_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.syncExcalidraw)
@@ -841,9 +874,21 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
this.containerEl.createEl("h1", { text: t("EXPERIMENTAL_HEAD") });
this.containerEl.createEl("p", { text: t("EXPERIMENTAL_DESC") });
new Setting(containerEl)
.setName(t("FIELD_SUGGESTOR_NAME"))
.setDesc(fragWithHTML(t("FIELD_SUGGESTOR_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.fieldSuggestor)
.onChange(async (value) => {
this.plugin.settings.fieldSuggestor = value;
this.applySettingsUpdate();
}),
);
new Setting(containerEl)
.setName(t("FILETYPE_NAME"))
.setDesc(t("FILETYPE_DESC"))
.setDesc(fragWithHTML(t("FILETYPE_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.experimentalFileType)
@@ -856,7 +901,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("FILETAG_NAME"))
.setDesc(t("FILETAG_DESC"))
.setDesc(fragWithHTML(t("FILETAG_DESC")))
.addText((text) =>
text
.setPlaceholder(t("INSERT_EMOJI"))
@@ -869,7 +914,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("LIVEPREVIEW_NAME"))
.setDesc(t("LIVEPREVIEW_DESC"))
.setDesc(fragWithHTML(t("LIVEPREVIEW_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.experimentalLivePreview)
@@ -881,7 +926,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("ENABLE_FOURTH_FONT_NAME"))
.setDesc(t("ENABLE_FOURTH_FONT_DESC"))
.setDesc(fragWithHTML(t("ENABLE_FOURTH_FONT_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.experimentalEnableFourthFont)
@@ -894,7 +939,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("FOURTH_FONT_NAME"))
.setDesc(t("FOURTH_FONT_DESC"))
.setDesc(fragWithHTML(t("FOURTH_FONT_DESC")))
.addDropdown(async (d: DropdownComponent) => {
d.addOption("Virgil", "Virgil");
this.app.vault
@@ -913,7 +958,13 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
);
});
if (Object.keys(this.plugin.settings.scriptEngineSettings).length > 0) {
const scripts = this.plugin.scriptEngine
.getListofScripts()
?.map((f) => this.plugin.scriptEngine.getScriptName(f));
if (
Object.keys(this.plugin.settings.scriptEngineSettings).length > 0 &&
scripts
) {
const getValue = (scriptName: string, variableName: string): any => {
const variable =
//@ts-ignore
@@ -1034,8 +1085,9 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
};
this.containerEl.createEl("h1", { text: t("SCRIPT_SETTINGS_HEAD") });
Object.keys(this.plugin.settings.scriptEngineSettings).forEach(
(scriptName: string) => {
Object.keys(this.plugin.settings.scriptEngineSettings)
.filter((s) => scripts.contains(s))
.forEach((scriptName: string) => {
const settings =
//@ts-ignore
this.plugin.settings.scriptEngineSettings[scriptName];
@@ -1084,8 +1136,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
break;
}
});
},
);
});
}
}
}

View File

@@ -134,4 +134,14 @@ li[data-testid] {
.excalidraw-scriptengine-install .modal {
max-height:90%;
}
.excalidraw-prompt-center {
text-align: center;
}
.excalidraw-prompt-center.filepath {
text-align: center;
font-weight: bold;
margin-bottom: 2em;
}

View File

@@ -1,4 +1,4 @@
{
"1.5.21": "0.12.16",
"1.5.30": "0.12.16",
"1.4.2": "0.11.13"
}

View File

@@ -1355,6 +1355,11 @@
"schema-utils" "^3.0.0"
"source-map" "^0.7.3"
"@popperjs/core@^2.11.2":
"integrity" "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA=="
"resolved" "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz"
"version" "2.11.2"
"@rollup/plugin-babel@^5.2.0", "@rollup/plugin-babel@^5.3.0":
"integrity" "sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw=="
"resolved" "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz"
@@ -2122,10 +2127,10 @@
"resolved" "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz"
"version" "4.2.2"
"@zsviczian/excalidraw@0.10.0-obsidian-37":
"integrity" "sha512-FssxK/xkDzsltu81aMTLkWVd0Te9EMV7H74K6GINF2M688rTk1Up5DGKIwI1fX8Zl+OobpmEBErctLLsQmb5jQ=="
"resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.10.0-obsidian-37.tgz"
"version" "0.10.0-obsidian-37"
"@zsviczian/excalidraw@0.10.0-obsidian-39":
"integrity" "sha512-8uUby+Wzt1lVGqG6231IKWssd1uL+ERBSqscjjI/15bIDrqdatwJIm4taSHjNjiasOTo8p/OjG/s2Aulkr4uug=="
"resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.10.0-obsidian-39.tgz"
"version" "0.10.0-obsidian-39"
dependencies:
"dotenv" "10.0.0"