Compare commits

..

54 Commits

Author SHA1 Message Date
Zsolt Viczian
1ca87741a5 1.6.14 2022-02-20 23:14:23 +01:00
zsviczian
463eb1d228 Update README.md 2022-02-19 23:32:49 +01:00
Zsolt Viczian
935febf337 disable save on addElementsToView to avoid the autozoom effect 2022-02-19 22:21:46 +01:00
Zsolt Viczian
e4c7fe7fc9 edit most recently drawn line 2022-02-19 20:00:09 +01:00
Zsolt Viczian
e98b1755ee 1.6.13 2022-02-19 18:16:00 +01:00
Zsolt Viczian
bc71bde9b7 1.6.12 2022-02-19 00:00:52 +01:00
zsviczian
2dc10af004 Update README.md 2022-02-18 13:22:32 +01:00
zsviczian
46d21f53e5 Merge pull request #452 from 1-2-3/master
feat: add detection angle function in script Repeat Elements
2022-02-18 10:15:00 +01:00
zahuifan
44d9b44c88 update demo image 2022-02-18 16:11:45 +08:00
zahuifan
5875f268f4 feat: add detection angle function in script 2022-02-18 15:58:19 +08:00
zsviczian
bc1120ac3a Update Toggle Fullscreen on Mobile.md 2022-02-18 07:43:55 +01:00
Zsolt Viczian
40a10620c1 added warning 2022-02-17 20:56:25 +01:00
Zsolt Viczian
28295deaa0 add thind header to allow command palette 2022-02-17 20:43:48 +01:00
Zsolt Viczian
b783d9b992 script toggle fullscreen 2022-02-17 20:34:13 +01:00
Zsolt Viczian
98df940f52 correct link 2022-02-17 20:04:01 +01:00
Zsolt Viczian
ab3b84a765 update script 2022-02-17 20:01:53 +01:00
Zsolt Viczian
5fc6905ac7 upload image 2022-02-17 19:59:29 +01:00
Zsolt Viczian
00c7a0e934 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2022-02-17 19:36:44 +01:00
Zsolt Viczian
60ed7f21f6 toggle fullscreen script 2022-02-17 19:36:26 +01:00
zsviczian
9e0c5c48d4 Update bug_report.md 2022-02-17 11:49:37 +01:00
zsviczian
7e76c2b693 Merge pull request #446 from 1-2-3/master
fix: line and freedraw do not scale correctly in repeat elements script
2022-02-17 11:20:08 +01:00
zahuifan
dd9b363427 fix: line and freedraw do not scale correctly 2022-02-17 16:51:59 +08:00
zahuifan
90b693151a fix: line and freedraw do not scale correctly 2022-02-17 16:30:53 +08:00
zsviczian
d280c7e953 Merge pull request #439 from 1-2-3/master
feat: new repeat elements script
2022-02-16 22:06:19 +01:00
zahuifan
b227ad05fd update repeat-elements demo picture 2022-02-16 16:49:54 +08:00
zahuifan
cf07458fbb fix: when alert return 2022-02-16 16:37:50 +08:00
zahuifan
c191b7264d feat: new repeat elements script 2022-02-16 16:17:19 +08:00
Zsolt Viczian
109fe05302 autozoom after new text, scriptstore style 2022-02-15 22:11:32 +01:00
Zsolt Viczian
d55bb1a2c4 organic line script 2022-02-15 21:40:28 +01:00
Zsolt Viczian
1362c7f416 add image 2022-02-15 21:31:35 +01:00
Zsolt Viczian
dc718bbfe1 1.6.11 2022-02-15 19:37:15 +01:00
zsviczian
3ab2a3d7fc Merge pull request #437 from 1-2-3/master
fix: await addElementsToView() in scripts
2022-02-15 13:55:24 +01:00
zahuifan
494556b501 Merge branch 'master' of github.com:1-2-3/obsidian-excalidraw-plugin 2022-02-15 19:28:00 +08:00
zahuifan
76b767f505 fix: await addElementsToView() in scripts 2022-02-15 19:26:55 +08:00
zsviczian
8178328ce8 Merge pull request #435 from 1-2-3/master
fix: only wraps the first element in 1.6.10
2022-02-15 10:55:27 +01:00
zsviczian
766bf25773 Update Box Each Selected Groups.md 2022-02-15 10:52:49 +01:00
zahuifan
ccf81efa7b fix: only wraps the first element in 1.6.10 2022-02-15 09:56:45 +08:00
Zsolt Viczian
4e101f8786 1.6.10 2022-02-14 21:50:59 +01:00
Zsolt Viczian
05a7ee8270 insert link/image invalid characters 2022-02-13 16:13:31 +01:00
Zsolt Viczian
0e5004ae08 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2022-02-13 14:02:09 +01:00
Zsolt Viczian
77f103d46e 1.6.9 2022-02-13 14:01:34 +01:00
zsviczian
e2ad92a19c Update README.md 2022-02-11 23:00:53 +01:00
Zsolt Viczian
0eb3bc6798 1.6.8 2022-02-11 22:51:28 +01:00
zsviczian
b11cf139a8 Update versions.json 2022-02-10 08:19:57 +01:00
zsviczian
81ce31dfbf Update manifest.json 2022-02-10 08:19:40 +01:00
zsviczian
e3f7455511 Update ExcalidrawView.ts 2022-02-10 08:17:46 +01:00
Zsolt Viczian
321a5e8ea1 1.6.6 2022-02-09 22:49:14 +01:00
Zsolt Viczian
b16362a6fd 1.6.5 2022-02-08 20:15:11 +01:00
Zsolt Viczian
a52f633755 1.6.4 2022-02-08 19:52:26 +01:00
Zsolt Viczian
65fe5282c4 1.6.3 2022-02-07 21:48:37 +01:00
Zsolt Viczian
4efa4866ef publish scribble helper 2022-02-06 23:37:06 +01:00
Zsolt Viczian
a11c195ea4 scribble script 2022-02-06 23:33:30 +01:00
Zsolt Viczian
9d31de7674 image 2022-02-06 23:30:11 +01:00
Zsolt Viczian
bf9e6e0179 1.6.2 2022-02-06 21:46:29 +01:00
67 changed files with 4209 additions and 2921 deletions

View File

@@ -23,16 +23,10 @@ A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Environment (please complete the following information):**
- OS including version: [e.g. iOS 15.1, Android 9, Windows 11, etc]
- Plugin version:
- Obsidian version:
**Additional context**
Add any other context about the problem here.

View File

@@ -14,6 +14,7 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
|[![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)|[![thumbnail](https://user-images.githubusercontent.com/14358394/151705333-54e9ffd2-0bd7-4d02-b99e-0bd4e4708d4d.jpg)](https://youtu.be/qbPIAZguJeo)|[![Thumbnail](https://user-images.githubusercontent.com/14358394/152585752-7eb0371f-0bab-40f6-a194-3b48e5811735.jpg)](https://youtu.be/2Y8OhkGiTHg)|
|[![Thumbnail](https://user-images.githubusercontent.com/14358394/153676009-6f86b2d7-c248-49a2-b802-be21c6999e4f.jpg)](https://youtu.be/2v9TZmQNO8c)|[![Thumbnail](https://user-images.githubusercontent.com/14358394/154821232-a404b6cf-72fb-4ce4-9d53-619132dce491.jpg)](https://youtu.be/xHPGWR3m0c8)||
# Key features
@@ -27,7 +28,7 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
- Compatibility features to auto-export and keep in sync markdown excalidraw files and legacy .excalidraw files.
- Experimental feature to add custom TAG to file explorer to mark drawing files.
- Enable / disable autosave.
- You can customize the size and position of the embedded images using the `![[image.excalidraw|100]]`, `![[image.excalidraw|100x100]]`, `![[image.excalidraw|100|left]]`, `![[image.excalidraw|right-wrap]]`, formatting options. `![[<filename.excalidraw>|<width>x<height>|<alignment>]]`. You can add your custom alignment via CSS. Any text that appears in `<alignment>` will be added to the rendered SVG element style and to the wrapper DIV element. Check below and styles.css for more insight.
- You can customize the size and position of the embedded images using the `![[image.excalidraw|100]]`, `![[image.excalidraw|100x100]]`, `![[image.excalidraw|100|left]]`, `![[image.excalidraw|right-wrap]]`, formatting options. `![[<filename.excalidraw>|<width>x<height>|<alignment>]]`. You can add your custom alignment via CSS. Any text that appears in `<alignment>` will be added to the rendered SVG element style and to the wrapper DIV element. See [styles.css](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/styles.css) for more insight.
- Supports hyperlinks e.g. `https://zsolt.blog`, `[Obsidian](https://obsidian.md)`, and internal links e.g. `[[My file in vault|Alias]]` in drawing text.
- Links will update when files are moved or renamed, if you have the Obsidian setting Files & Links/Automatically Update Internal Links enabled.
- Links in drawings will show up in backlinks of documents

View File

@@ -195,6 +195,12 @@ export interface ExcalidrawAutomate {
selectElementsInView(elements: ExcalidrawElement[]): void; //sets selection in view
generateElementId(): string; //returns an 8 character long random id
cloneElement(element: ExcalidrawElement): ExcalidrawElement; //Returns a clone of the element with a new id
moveViewElementToZIndex(elementId:number, newZIndex:number): void; //Moves the element to a specific position in the z-index
hexStringToRgb(color: string):number[];
rgbToHexString(color: number[]):string;
hslToRgb(color: number[]):number[];
rgbToHsl(color:number[]):number[];
colorNameToHex(color:string):string;
}
```

View File

@@ -22,4 +22,4 @@ elements.forEach((el)=>{
);
ea.addToGroup([el.id,ellipseId]);
});
ea.addElementsToView();
ea.addElementsToView(false,false);

View File

@@ -41,7 +41,7 @@ if(!file) return;
const link = `[[${app.metadataCache.fileToLinktext(file,ea.targetView.file.path,true)}]]`;
ea.style.backgroundColor = "transparent";
ea.style.strokeColor = "steelblue"
ea.style.strokeColor = "rgba(70,130,180,0.05)"
ea.style.strokeWidth = 2;
ea.style.roughness = 0;
@@ -52,8 +52,8 @@ if(elements.lenght===1 && elements[0].type !== "text") {
const b = ea.getBoundingBox(elements);
const id = ea.addEllipse(b.topX+b.width-5, b.topY, 5, 5);
ea.getElement(id).link = link;
/* ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup(elements.map((e)=>e.id).concat([id]));*/
ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup(elements.map((e)=>e.id).concat([id]));
}
await ea.addElementsToView(false,true,true);
ea.selectElementsInView(ea.getElements());

View File

@@ -67,7 +67,7 @@ if(file && fileType==="ex") {
const link = `[[${app.metadataCache.fileToLinktext(file,ea.targetView.file.path,true)}]]`;
ea.style.backgroundColor = "transparent";
ea.style.strokeColor = "steelblue"
ea.style.strokeColor = "rgba(70,130,180,0.05)"
ea.style.strokeWidth = 2;
ea.style.roughness = 0;
@@ -78,8 +78,8 @@ if(elements.lenght===1 && elements[0].type !== "text") {
const b = ea.getBoundingBox(elements);
const id = ea.addEllipse(b.topX+b.width-5, b.topY, 5, 5);
ea.getElement(id).link = link;
/* ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup(elements.map((e)=>e.id).concat([id]));*/
ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup(elements.map((e)=>e.id).concat([id]));
}
await ea.addElementsToView(false,true,true);
ea.selectElementsInView(ea.getElements());

View File

@@ -113,7 +113,7 @@ if(!isFirst) {
rect.backgroundColor = fromElement.backgroundColor;
rect.fillStyle = fromElement.fillStyle;
await ea.addElementsToView(false);
await ea.addElementsToView(false,false);
} else {
id = ea.addText(
0,
@@ -127,7 +127,7 @@ if(!isFirst) {
...fixWidth?{width: width}:null
}
);
await ea.addElementsToView(true);
await ea.addElementsToView(true,false);
}
ea.selectElementsInView([ea.getElement(id)]);

View File

@@ -130,10 +130,10 @@ for(const elements of groups) {
ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup([id].concat(elements.map((el)=>el.id)));
ea.addElementsToView(false);
ea.reset();
}
await ea.addElementsToView(false,false);
function recalculateStartPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[1][0];
@@ -178,4 +178,4 @@ function recalculateEndPointOfLine(line, el) {
if(intersectA.length > 0) {
line.points[line.points.length - 1] = [intersectA[0][0] - line.x, intersectA[0][1] - line.y];
}
}
}

View File

@@ -52,4 +52,4 @@ id = ea.addRect(
);
ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup([id].concat(elements.map((el)=>el.id)));
ea.addElementsToView(false);
ea.addElementsToView(false,false);

View File

@@ -5,12 +5,36 @@ 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(shapesDispaly, shapes);
if(!newShape) return;
const boxShapesDispaly=["○ ellipse","□ rectangle","◇ diamond"];
const boxShapes=["ellipse","rectangle","diamond"];
const lineShapesDispaly=["- line","⭢ arrow"];
const lineShapes=["line","arrow"];
elements.forEach(el=>el.type = newShape);
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
let editedElements = [];
let elements = ea.getViewSelectedElements().filter(el=>boxShapes.contains(el.type));
if (elements.length>0) {
newShape = await utils.suggester(boxShapesDispaly, boxShapes, "Change shape of 'box' type elements in selection");
if(newShape) {
editedElements = elements;
elements.forEach(el=>el.type = newShape);
}
}
elements = ea.getViewSelectedElements().filter(el=>lineShapes.contains(el.type));
if (elements.length>0) {
newShape = await utils.suggester(lineShapesDispaly, lineShapes, "Change shape of 'line' type elements in selection");
if(newShape) {
editedElements = editedElements.concat(elements);
elements.forEach((el)=>{
el.type = newShape;
if(newShape === "arrow") {
el.endArrowhead = "triangle";
}
});
}
}
ea.copyViewElementsToEAforEditing(editedElements);
ea.addElementsToView(false,false);

View File

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

View File

@@ -60,5 +60,5 @@ elements.forEach((el)=>{
});
ea.deleteViewElements(elements);
await ea.addElementsToView(false,true,true);
await ea.addElementsToView(false,false,true);
ea.selectElementsInView(ea.getElements());

View File

@@ -48,4 +48,4 @@ elements.forEach((el)=>{
el.containerId = id;
});
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
ea.addElementsToView(false,false);

View File

@@ -40,7 +40,7 @@ for (const el of ea.getElements()) {
el.backgroundColor = "#" + rgbToHexString(newRgb);
}
}
ea.addElementsToView(false, false);
await ea.addElementsToView(false, false);
function rgbToHexString(args) {
const integer =

View File

@@ -1,56 +1,56 @@
/*
![](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/elbow-connectors.png)
This script converts the selected connectors to elbows.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const lines = elements.filter((el)=>el.type==="arrow" || el.type==="line");
for (const line of lines) {
if (line.points.length >= 3) {
for (var i = 0; i < line.points.length - 2; i++) {
var p1;
var p3;
if (line.points[i][0] < line.points[i + 2][0]) {
p1 = line.points[i];
p3 = line.points[i+2];
} else {
p1 = line.points[i + 2];
p3 = line.points[i];
}
const p2 = line.points[i + 1];
if (p1[0] === p3[0]) {
continue;
}
const k = (p3[1] - p1[1]) / (p3[0] - p1[0]);
const b = p1[1] - k * p1[0];
y0 = k * p2[0] + b;
const up = p2[1] < y0;
if ((k > 0 && !up) || (k < 0 && up)) {
p2[0] = p1[0];
p2[1] = p3[1];
} else {
p2[0] = p3[0];
p2[1] = p1[1];
}
}
}
}
ea.copyViewElementsToEAforEditing(lines);
ea.addElementsToView();
/*
![](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/elbow-connectors.png)
This script converts the selected connectors to elbows.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const lines = elements.filter((el)=>el.type==="arrow" || el.type==="line");
for (const line of lines) {
if (line.points.length >= 3) {
for (var i = 0; i < line.points.length - 2; i++) {
var p1;
var p3;
if (line.points[i][0] < line.points[i + 2][0]) {
p1 = line.points[i];
p3 = line.points[i+2];
} else {
p1 = line.points[i + 2];
p3 = line.points[i];
}
const p2 = line.points[i + 1];
if (p1[0] === p3[0]) {
continue;
}
const k = (p3[1] - p1[1]) / (p3[0] - p1[0]);
const b = p1[1] - k * p1[0];
y0 = k * p2[0] + b;
const up = p2[1] < y0;
if ((k > 0 && !up) || (k < 0 && up)) {
p2[0] = p1[0];
p2[1] = p3[1];
} else {
p2[0] = p3[0];
p2[1] = p1[1];
}
}
}
}
ea.copyViewElementsToEAforEditing(lines);
await ea.addElementsToView(false,false);

View File

@@ -1,146 +1,146 @@
/*
![](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-expand-rectangles.gif)
This script expands the width of the selected rectangles until they are all the same width and keep the text centered.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow')) ?
[...result, group[0]] : result, []);
const groupWidths = topGroups
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minLeft: 0, maxRight: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minLeft: cur.x,
maxRight: cur.x + cur.width,
index: i,
};
} else {
return {
minLeft: cur.x < pre.minLeft ? cur.x : pre.minLeft,
maxRight:
cur.x + cur.width > pre.maxRight
? cur.x + cur.width
: pre.maxRight,
index: i,
};
}
},
{ minLeft: 0, maxRight: 0 }
);
})
.map((r) => {
r.width = r.maxRight - r.minLeft;
return r;
});
const maxGroupWidth = Math.max(...groupWidths.map((g) => g.width));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.x - rha.x);
const texts = topGroups[i]
.filter((el) => el.type === "text")
.sort((lha, rha) => lha.x - rha.x);
const groupWith = groupWidths[i].width;
if (groupWith < maxGroupWidth) {
const distance = maxGroupWidth - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
const rectLeft = rect.x;
const rectTop = rect.y;
const rectRight = rect.x + rect.width;
const rectBottom = rect.y + rect.height;
rect.x = rect.x + perRectDistance * j - perRectDistance / 2;
rect.width += perRectDistance;
const textsWithRect = texts.filter(text => text.x >= rectLeft && text.x <= rectRight
&& text.y >= rectTop && text.y <= rectBottom);
for(const text of textsWithRect) {
text.x = text.x + perRectDistance * j;
}
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);
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-expand-rectangles.gif)
This script expands the width of the selected rectangles until they are all the same width and keep the text centered.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow')) ?
[...result, group[0]] : result, []);
const groupWidths = topGroups
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minLeft: 0, maxRight: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minLeft: cur.x,
maxRight: cur.x + cur.width,
index: i,
};
} else {
return {
minLeft: cur.x < pre.minLeft ? cur.x : pre.minLeft,
maxRight:
cur.x + cur.width > pre.maxRight
? cur.x + cur.width
: pre.maxRight,
index: i,
};
}
},
{ minLeft: 0, maxRight: 0 }
);
})
.map((r) => {
r.width = r.maxRight - r.minLeft;
return r;
});
const maxGroupWidth = Math.max(...groupWidths.map((g) => g.width));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.x - rha.x);
const texts = topGroups[i]
.filter((el) => el.type === "text")
.sort((lha, rha) => lha.x - rha.x);
const groupWith = groupWidths[i].width;
if (groupWith < maxGroupWidth) {
const distance = maxGroupWidth - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
const rectLeft = rect.x;
const rectTop = rect.y;
const rectRight = rect.x + rect.width;
const rectBottom = rect.y + rect.height;
rect.x = rect.x + perRectDistance * j - perRectDistance / 2;
rect.width += perRectDistance;
const textsWithRect = texts.filter(text => text.x >= rectLeft && text.x <= rectRight
&& text.y >= rectTop && text.y <= rectBottom);
for(const text of textsWithRect) {
text.x = text.x + perRectDistance * j;
}
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
await ea.addElementsToView(false, false);
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

@@ -1,133 +1,133 @@
/*
![](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-expand-rectangles.gif)
This script expands the width of the selected rectangles until they are all the same width.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
const groupWidths = topGroups
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minLeft: 0, maxRight: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minLeft: cur.x,
maxRight: cur.x + cur.width,
index: i,
};
} else {
return {
minLeft: cur.x < pre.minLeft ? cur.x : pre.minLeft,
maxRight:
cur.x + cur.width > pre.maxRight
? cur.x + cur.width
: pre.maxRight,
index: i,
};
}
},
{ minLeft: 0, maxRight: 0 }
);
})
.map((r) => {
r.width = r.maxRight - r.minLeft;
return r;
});
const maxGroupWidth = Math.max(...groupWidths.map((g) => g.width));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.x - rha.x);
const groupWith = groupWidths[i].width;
if (groupWith < maxGroupWidth) {
const distance = maxGroupWidth - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
rect.x = rect.x + perRectDistance * j;
rect.width += perRectDistance;
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);
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-expand-rectangles.gif)
This script expands the width of the selected rectangles until they are all the same width.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
const groupWidths = topGroups
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minLeft: 0, maxRight: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minLeft: cur.x,
maxRight: cur.x + cur.width,
index: i,
};
} else {
return {
minLeft: cur.x < pre.minLeft ? cur.x : pre.minLeft,
maxRight:
cur.x + cur.width > pre.maxRight
? cur.x + cur.width
: pre.maxRight,
index: i,
};
}
},
{ minLeft: 0, maxRight: 0 }
);
})
.map((r) => {
r.width = r.maxRight - r.minLeft;
return r;
});
const maxGroupWidth = Math.max(...groupWidths.map((g) => g.width));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.x - rha.x);
const groupWith = groupWidths[i].width;
if (groupWith < maxGroupWidth) {
const distance = maxGroupWidth - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
rect.x = rect.x + perRectDistance * j;
rect.width += perRectDistance;
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
await ea.addElementsToView(false, false);
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

@@ -1,146 +1,146 @@
/*
![](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-expand-rectangles.gif)
This script expands the height of the selected rectangles until they are all the same height and keep the text centered.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
const groupHeights = topGroups
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minTop: 0, maxBottom: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minTop: cur.y,
maxBottom: cur.y + cur.height,
index: i,
};
} else {
return {
minTop: cur.y < pre.minTop ? cur.y : pre.minTop,
maxBottom:
cur.y + cur.height > pre.maxBottom
? cur.y + cur.height
: pre.maxBottom,
index: i,
};
}
},
{ minTop: 0, maxBottom: 0 }
);
})
.map((r) => {
r.height = r.maxBottom - r.minTop;
return r;
});
const maxGroupHeight = Math.max(...groupHeights.map((g) => g.height));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.y - rha.y);
const texts = topGroups[i]
.filter((el) => el.type === "text")
.sort((lha, rha) => lha.y - rha.y);
const groupWith = groupHeights[i].height;
if (groupWith < maxGroupHeight) {
const distance = maxGroupHeight - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
const rectLeft = rect.x;
const rectTop = rect.y;
const rectRight = rect.x + rect.width;
const rectBottom = rect.y + rect.height;
rect.y = rect.y + perRectDistance * j - perRectDistance / 2;
rect.height += perRectDistance;
const textsWithRect = texts.filter(text => text.x >= rectLeft && text.x <= rectRight
&& text.y >= rectTop && text.y <= rectBottom);
for(const text of textsWithRect) {
text.y = text.y + perRectDistance * j;
}
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);
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-expand-rectangles.gif)
This script expands the height of the selected rectangles until they are all the same height and keep the text centered.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
const groupHeights = topGroups
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minTop: 0, maxBottom: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minTop: cur.y,
maxBottom: cur.y + cur.height,
index: i,
};
} else {
return {
minTop: cur.y < pre.minTop ? cur.y : pre.minTop,
maxBottom:
cur.y + cur.height > pre.maxBottom
? cur.y + cur.height
: pre.maxBottom,
index: i,
};
}
},
{ minTop: 0, maxBottom: 0 }
);
})
.map((r) => {
r.height = r.maxBottom - r.minTop;
return r;
});
const maxGroupHeight = Math.max(...groupHeights.map((g) => g.height));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.y - rha.y);
const texts = topGroups[i]
.filter((el) => el.type === "text")
.sort((lha, rha) => lha.y - rha.y);
const groupWith = groupHeights[i].height;
if (groupWith < maxGroupHeight) {
const distance = maxGroupHeight - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
const rectLeft = rect.x;
const rectTop = rect.y;
const rectRight = rect.x + rect.width;
const rectBottom = rect.y + rect.height;
rect.y = rect.y + perRectDistance * j - perRectDistance / 2;
rect.height += perRectDistance;
const textsWithRect = texts.filter(text => text.x >= rectLeft && text.x <= rectRight
&& text.y >= rectTop && text.y <= rectBottom);
for(const text of textsWithRect) {
text.y = text.y + perRectDistance * j;
}
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
await ea.addElementsToView(false, false);
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

@@ -1,131 +1,131 @@
/*
![](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-expand-rectangles.gif)
This script expands the height of the selected rectangles until they are all the same height.
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allLines = ea.getViewElements().filter(el => el.type === 'arrow' || el.type === 'line');
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
const groupHeights = topGroups
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minTop: 0, maxBottom: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minTop: cur.y,
maxBottom: cur.y + cur.height,
index: i,
};
} else {
return {
minTop: cur.y < pre.minTop ? cur.y : pre.minTop,
maxBottom:
cur.y + cur.height > pre.maxBottom
? cur.y + cur.height
: pre.maxBottom,
index: i,
};
}
},
{ minTop: 0, maxBottom: 0 }
);
})
.map((r) => {
r.height = r.maxBottom - r.minTop;
return r;
});
const maxGroupHeight = Math.max(...groupHeights.map((g) => g.height));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.y - rha.y);
const groupWith = groupHeights[i].height;
if (groupWith < maxGroupHeight) {
const distance = maxGroupHeight - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
rect.y = rect.y + perRectDistance * j;
rect.height += perRectDistance;
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);
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-expand-rectangles.gif)
This script expands the height of the selected rectangles until they are all the same height.
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allLines = ea.getViewElements().filter(el => el.type === 'arrow' || el.type === 'line');
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
const groupHeights = topGroups
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minTop: 0, maxBottom: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minTop: cur.y,
maxBottom: cur.y + cur.height,
index: i,
};
} else {
return {
minTop: cur.y < pre.minTop ? cur.y : pre.minTop,
maxBottom:
cur.y + cur.height > pre.maxBottom
? cur.y + cur.height
: pre.maxBottom,
index: i,
};
}
},
{ minTop: 0, maxBottom: 0 }
);
})
.map((r) => {
r.height = r.maxBottom - r.minTop;
return r;
});
const maxGroupHeight = Math.max(...groupHeights.map((g) => g.height));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.y - rha.y);
const groupWith = groupHeights[i].height;
if (groupWith < maxGroupHeight) {
const distance = maxGroupHeight - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
rect.y = rect.y + perRectDistance * j;
rect.height += perRectDistance;
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
await ea.addElementsToView(false, false);
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

@@ -69,4 +69,4 @@ for(var i=0; i<groups.length; i++) {
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);
await ea.addElementsToView(false, false);

View File

@@ -123,4 +123,4 @@ else if(fromBorder === 'right') {
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);
await ea.addElementsToView(false, false);

View File

@@ -64,4 +64,4 @@ for(var i=0; i<groups.length; i++) {
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);
await ea.addElementsToView(false, false);

View File

@@ -71,4 +71,4 @@ for(var i=0; i<groups.length; i++) {
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);
await ea.addElementsToView(false, false);

View File

@@ -64,4 +64,4 @@ for(var i=0; i<groups.length; i++) {
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);
await ea.addElementsToView(false, false);

View File

@@ -40,7 +40,7 @@ for (const el of ea.getElements()) {
el.backgroundColor = "#" + rgbToHexString(newRgb);
}
}
ea.addElementsToView(false, false);
await ea.addElementsToView(false, false);
function rgbToHexString(args) {
const integer =

View File

@@ -64,7 +64,7 @@ ea.getElements().forEach((el)=>{
}
}
});
ea.addElementsToView(false, false);
await ea.addElementsToView(false, false);
function colorNameToHex(color) {
const colors = {

View File

@@ -25,7 +25,7 @@ for(const arrow of selectedIndividualArrows) {
}
ea.copyViewElementsToEAforEditing(selectedIndividualArrows);
ea.addElementsToView();
await ea.addElementsToView(false,false);
function recalculateStartPointOfLine(line, el, elB) {
const aX = el.x + el.width/2;

View File

@@ -0,0 +1,27 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-organic-line.jpg)
Converts selected freedraw lines such that pencil pressure will decrease from maximum to minimum from the beginning of the line to its end. The resulting line is placed at the back of the layers, under all other items. Helpful when drawing organic mindmaps.
```javascript
*/
let elements = ea.getViewSelectedElements().filter((el)=>["freedraw","line","arrow"].includes(el.type));
if(elements.length === 0) {
elements = ea.getViewSelectedElements();
const len = elements.length;
if(len === 0 || ["freedraw","line","arrow"].includes(elements[len].type)) {
return;
}
elements = [elements[len]];
}
elements.forEach((el)=>{
el.simulatePressure = false;
el.type = "freedraw";
el.pressures = [];
const len = el.points.length;
for(i=0;i<len;i++)
el.pressures.push((len-i)/len);
});
ea.copyViewElementsToEAforEditing(elements);
await ea.addElementsToView(false,false);
elements.forEach((el)=>ea.moveViewElementToZIndex(el.id,0));

View File

@@ -42,7 +42,10 @@ Open the script you are interested in and save it to your Obsidian Vault includi
|[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)|
|[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)|
|[Organic Line](Organic%20Line.md)|Converts selected freedraw lines such that pencil pressure will decrease from maximum to minimum from the beginning of the line to its end. The resulting line is placed at the back of the layers, under all other items. Helpful when drawing organic mindmaps.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-organic-line.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Repeat Elements](Repeat%20Elements.md)|This script will detect the difference between 2 selected elements, including position, size, angle, stroke and background color, and create several elements that repeat these differences based on the number of repetitions entered by the user.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-repeat-elements.png)|[@1-2-3](https://github.com/1-2-3)|
|[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)|
|[Scribble Helper](Scribble%20Helper.md)|iOS scribble helper for better handwriting experience with text elements. If no elements are selected then the creates a text element at pointer position and you can use the edit box to modify the text with scribble. If a text element is selected then opens the input prompt where you can modify this text with scribble.|![]('https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-scribble-helper.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)|
@@ -52,5 +55,6 @@ Open the script you are interested in and save it to your Obsidian Vault includi
|[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)|
|[Toggle Fullscreen on Mobile](Toggle%20Fullscreen%20on%20Mobile.md)|Hides Obsidian workspace leaf padding and header (based on option in settings, default is "hide header" = false) which will take Excalidraw to full screen. ⚠ Note that if the header is not visible, it will be very difficult to invoke the command palette to end full screen. Only hide the header if you have a keyboard or you've practiced opening command palette!|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/ea-toggle-fullscreen.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)|
|[Zoom to Fit Selected Elements](Zoom%20to%20Fit%20Selected%20Elements.md)|Similar to Excalidraw standard SHIFT+2 feature: Zoom to fit selected elements, but with the ability to zoom to 1000%. Inspiration: [#272](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/272)||[@zsviczian](https://github.com/zsviczian)|

View File

@@ -0,0 +1,378 @@
/*
![](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-repeat-elements.png)
This script will detect the difference between 2 selected elements, including position, size, angle, stroke and background color, and create several elements that repeat these differences based on the number of repetitions entered by the user.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
let repeatNum = parseInt(await utils.inputPrompt("repeat times?","number","5"));
if(!repeatNum) {
new Notice("Please enter a number.");
return;
}
const selectedElements = ea.getViewSelectedElements().sort((lha,rha) =>
lha.x === rha.x? (lha.y === rha.y?
(lha.width === rha.width?
(lha.height - rha.height) : lha.width - rha.width)
: lha.y - rha.y) : lha.x - rha.x);
if(selectedElements.length !== 2) {
new Notice("Please select 2 elements.");
return;
}
if(selectedElements[0].type !== selectedElements[1].type) {
new Notice("The selected elements must be of the same type.");
return;
}
const xDistance = selectedElements[1].x - selectedElements[0].x;
const yDistance = selectedElements[1].y - selectedElements[0].y;
const widthDistance = selectedElements[1].width - selectedElements[0].width;
const heightDistance = selectedElements[1].height - selectedElements[0].height;
const angleDistance = selectedElements[1].angle - selectedElements[0].angle;
const bgColor1 = colorNameToHex(selectedElements[0].backgroundColor);
const rgbBgColor1 = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(bgColor1);
const bgColor2 = colorNameToHex(selectedElements[1].backgroundColor);
const rgbBgColor2 = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(bgColor2);
let bgHDistance = 0;
let bgSDistance = 0;
let bgLDistance = 0;
if(rgbBgColor1 && rgbBgColor2) {
const bgHsl1 = rgbToHsl([parseInt(rgbBgColor1[1], 16), parseInt(rgbBgColor1[2], 16), parseInt(rgbBgColor1[3], 16)]);
const bgHsl2 = rgbToHsl([parseInt(rgbBgColor2[1], 16), parseInt(rgbBgColor2[2], 16), parseInt(rgbBgColor2[3], 16)]);
bgHDistance = bgHsl2[0] - bgHsl1[0];
bgSDistance = bgHsl2[1] - bgHsl1[1];
bgLDistance = bgHsl2[2] - bgHsl1[2];
}
const strokeColor1 = colorNameToHex(selectedElements[0].strokeColor);
const rgbStrokeColor1 = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(strokeColor1);
const strokeColor2 = colorNameToHex(selectedElements[1].strokeColor);
const rgbStrokeColor2 = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(strokeColor2);
let strokeHDistance = 0;
let strokeSDistance = 0;
let strokeLDistance = 0;
if(rgbStrokeColor1 && rgbStrokeColor2) {
const strokeHsl1 = rgbToHsl([parseInt(rgbStrokeColor1[1], 16), parseInt(rgbStrokeColor1[2], 16), parseInt(rgbStrokeColor1[3], 16)]);
const strokeHsl2 = rgbToHsl([parseInt(rgbStrokeColor2[1], 16), parseInt(rgbStrokeColor2[2], 16), parseInt(rgbStrokeColor2[3], 16)]);
strokeHDistance = strokeHsl2[0] - strokeHsl1[0];
strokeSDistance = strokeHsl2[1] - strokeHsl1[1];
strokeLDistance = strokeHsl2[2] - strokeHsl1[2];
}
ea.copyViewElementsToEAforEditing(selectedElements);
for(let i=0; i<repeatNum; i++) {
const newEl = ea.cloneElement(selectedElements[1]);
ea.elementsDict[newEl.id] = newEl;
newEl.x += xDistance * (i + 1);
newEl.y += yDistance * (i + 1);
newEl.angle += angleDistance * (i + 1);
const originWidth = newEl.width;
const originHeight = newEl.height;
const newWidth = newEl.width + widthDistance * (i + 1);
const newHeight = newEl.height + heightDistance * (i + 1);
if(newWidth >= 0 && newHeight >= 0) {
if(newEl.type === 'arrow' || newEl.type === 'line' || newEl.type === 'freedraw') {
const minX = Math.min(...newEl.points.map(pt => pt[0]));
const minY = Math.min(...newEl.points.map(pt => pt[1]));
for(let j = 0; j < newEl.points.length; j++) {
if(newEl.points[j][0] > minX) {
newEl.points[j][0] = newEl.points[j][0] + ((newEl.points[j][0] - minX) / originWidth) * (newWidth - originWidth);
}
if(newEl.points[j][1] > minY) {
newEl.points[j][1] = newEl.points[j][1] + ((newEl.points[j][1] - minY) / originHeight) * (newHeight - originHeight);
}
}
}
else {
newEl.width = newWidth;
newEl.height = newHeight;
}
}
if(rgbBgColor1 && rgbBgColor2) {
const bgHsl2 = rgbToHsl([parseInt(rgbBgColor2[1], 16), parseInt(rgbBgColor2[2], 16), parseInt(rgbBgColor2[3], 16)]);
const newBgH = bgHsl2[0] + bgHDistance * (i + 1);
const newBgS = bgHsl2[1] + bgSDistance * (i + 1);
const newBgL = bgHsl2[2] + bgLDistance * (i + 1);
if(newBgH >= 0 && newBgH <= 360 && newBgS >= 0 && newBgS <= 100 && newBgL >= 0 && newBgL <= 100) {
const newBgRgb = hslToRgb([newBgH, newBgS, newBgL]);
newEl.backgroundColor = "#" + rgbToHexString(newBgRgb);
}
}
if(rgbStrokeColor1 && rgbStrokeColor2) {
const strokeHsl2 = rgbToHsl([parseInt(rgbStrokeColor2[1], 16), parseInt(rgbStrokeColor2[2], 16), parseInt(rgbStrokeColor2[3], 16)]);
const newStrokeH = strokeHsl2[0] + strokeHDistance * (i + 1);
const newStrokeS = strokeHsl2[1] + strokeSDistance * (i + 1);
const newStrokeL = strokeHsl2[2] + strokeLDistance * (i + 1);
if(newStrokeH >= 0 && newStrokeH <= 360 && newStrokeS >= 0 && newStrokeS <= 100 && newStrokeL >= 0 && newStrokeL <= 100) {
const newStrokeRgb = hslToRgb([newStrokeH, newStrokeS, newStrokeL]);
newEl.strokeColor = "#" + rgbToHexString(newStrokeRgb);
}
}
}
await ea.addElementsToView(false, false, true);
function rgbToHexString(args) {
const integer =
((Math.round(args[0]) & 0xff) << 16) +
((Math.round(args[1]) & 0xff) << 8) +
(Math.round(args[2]) & 0xff);
const string = integer.toString(16).toUpperCase();
return "000000".substring(string.length) + string;
}
function hslToRgb(hsl) {
const h = hsl[0] / 360;
const s = hsl[1] / 100;
const l = hsl[2] / 100;
let t2;
let t3;
let val;
if (s === 0) {
val = l * 255;
return [val, val, val];
}
if (l < 0.5) {
t2 = l * (1 + s);
} else {
t2 = l + s - l * s;
}
const t1 = 2 * l - t2;
const rgb = [0, 0, 0];
for (let i = 0; i < 3; i++) {
t3 = h + (1 / 3) * -(i - 1);
if (t3 < 0) {
t3++;
}
if (t3 > 1) {
t3--;
}
if (6 * t3 < 1) {
val = t1 + (t2 - t1) * 6 * t3;
} else if (2 * t3 < 1) {
val = t2;
} else if (3 * t3 < 2) {
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
} else {
val = t1;
}
rgb[i] = val * 255;
}
return rgb;
}
function rgbToHsl(rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const min = Math.min(r, g, b);
const max = Math.max(r, g, b);
const delta = max - min;
let h;
let s;
if (max === min) {
h = 0;
} else if (r === max) {
h = (g - b) / delta;
} else if (g === max) {
h = 2 + (b - r) / delta;
} else if (b === max) {
h = 4 + (r - g) / delta;
}
h = Math.min(h * 60, 360);
if (h < 0) {
h += 360;
}
const l = (min + max) / 2;
if (max === min) {
s = 0;
} else if (l <= 0.5) {
s = delta / (max + min);
} else {
s = delta / (2 - max - min);
}
return [h, s * 100, l * 100];
}
function colorNameToHex(color) {
const colors = {
aliceblue: "#f0f8ff",
antiquewhite: "#faebd7",
aqua: "#00ffff",
aquamarine: "#7fffd4",
azure: "#f0ffff",
beige: "#f5f5dc",
bisque: "#ffe4c4",
black: "#000000",
blanchedalmond: "#ffebcd",
blue: "#0000ff",
blueviolet: "#8a2be2",
brown: "#a52a2a",
burlywood: "#deb887",
cadetblue: "#5f9ea0",
chartreuse: "#7fff00",
chocolate: "#d2691e",
coral: "#ff7f50",
cornflowerblue: "#6495ed",
cornsilk: "#fff8dc",
crimson: "#dc143c",
cyan: "#00ffff",
darkblue: "#00008b",
darkcyan: "#008b8b",
darkgoldenrod: "#b8860b",
darkgray: "#a9a9a9",
darkgreen: "#006400",
darkkhaki: "#bdb76b",
darkmagenta: "#8b008b",
darkolivegreen: "#556b2f",
darkorange: "#ff8c00",
darkorchid: "#9932cc",
darkred: "#8b0000",
darksalmon: "#e9967a",
darkseagreen: "#8fbc8f",
darkslateblue: "#483d8b",
darkslategray: "#2f4f4f",
darkturquoise: "#00ced1",
darkviolet: "#9400d3",
deeppink: "#ff1493",
deepskyblue: "#00bfff",
dimgray: "#696969",
dodgerblue: "#1e90ff",
firebrick: "#b22222",
floralwhite: "#fffaf0",
forestgreen: "#228b22",
fuchsia: "#ff00ff",
gainsboro: "#dcdcdc",
ghostwhite: "#f8f8ff",
gold: "#ffd700",
goldenrod: "#daa520",
gray: "#808080",
green: "#008000",
greenyellow: "#adff2f",
honeydew: "#f0fff0",
hotpink: "#ff69b4",
"indianred ": "#cd5c5c",
indigo: "#4b0082",
ivory: "#fffff0",
khaki: "#f0e68c",
lavender: "#e6e6fa",
lavenderblush: "#fff0f5",
lawngreen: "#7cfc00",
lemonchiffon: "#fffacd",
lightblue: "#add8e6",
lightcoral: "#f08080",
lightcyan: "#e0ffff",
lightgoldenrodyellow: "#fafad2",
lightgrey: "#d3d3d3",
lightgreen: "#90ee90",
lightpink: "#ffb6c1",
lightsalmon: "#ffa07a",
lightseagreen: "#20b2aa",
lightskyblue: "#87cefa",
lightslategray: "#778899",
lightsteelblue: "#b0c4de",
lightyellow: "#ffffe0",
lime: "#00ff00",
limegreen: "#32cd32",
linen: "#faf0e6",
magenta: "#ff00ff",
maroon: "#800000",
mediumaquamarine: "#66cdaa",
mediumblue: "#0000cd",
mediumorchid: "#ba55d3",
mediumpurple: "#9370d8",
mediumseagreen: "#3cb371",
mediumslateblue: "#7b68ee",
mediumspringgreen: "#00fa9a",
mediumturquoise: "#48d1cc",
mediumvioletred: "#c71585",
midnightblue: "#191970",
mintcream: "#f5fffa",
mistyrose: "#ffe4e1",
moccasin: "#ffe4b5",
navajowhite: "#ffdead",
navy: "#000080",
oldlace: "#fdf5e6",
olive: "#808000",
olivedrab: "#6b8e23",
orange: "#ffa500",
orangered: "#ff4500",
orchid: "#da70d6",
palegoldenrod: "#eee8aa",
palegreen: "#98fb98",
paleturquoise: "#afeeee",
palevioletred: "#d87093",
papayawhip: "#ffefd5",
peachpuff: "#ffdab9",
peru: "#cd853f",
pink: "#ffc0cb",
plum: "#dda0dd",
powderblue: "#b0e0e6",
purple: "#800080",
rebeccapurple: "#663399",
red: "#ff0000",
rosybrown: "#bc8f8f",
royalblue: "#4169e1",
saddlebrown: "#8b4513",
salmon: "#fa8072",
sandybrown: "#f4a460",
seagreen: "#2e8b57",
seashell: "#fff5ee",
sienna: "#a0522d",
silver: "#c0c0c0",
skyblue: "#87ceeb",
slateblue: "#6a5acd",
slategray: "#708090",
snow: "#fffafa",
springgreen: "#00ff7f",
steelblue: "#4682b4",
tan: "#d2b48c",
teal: "#008080",
thistle: "#d8bfd8",
tomato: "#ff6347",
turquoise: "#40e0d0",
violet: "#ee82ee",
wheat: "#f5deb3",
white: "#ffffff",
whitesmoke: "#f5f5f5",
yellow: "#ffff00",
yellowgreen: "#9acd32",
};
if (typeof colors[color.toLowerCase()] != "undefined")
return colors[color.toLowerCase()];
return color;
}

View File

@@ -20,4 +20,4 @@ elements.forEach((el)=>{
el.endArrowhead = start;
});
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
ea.addElementsToView(false,false);

View File

@@ -0,0 +1,29 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-scribble-helper.jpg)
iOS scribble helper for better handwriting experience with text elements. If no elements are selected then the creates a text element at pointer position and you can use the edit box to modify the text with scribble. If a text element is selected then opens the input prompt where you can modify this text with scribble.
```javascript
*/
elements = ea.getViewSelectedElements().filter(el=>el.type==="text");
if(elements.length > 1) {
new Notice ("Select only 1 or 0 text elements.")
return;
}
const text = await utils.inputPrompt("Edit text","",(elements.length === 1)?elements[0].rawText:"");
if(!text) return;
if(elements.length === 1) {
ea.copyViewElementsToEAforEditing(elements);
ea.getElements()[0].originalText = text;
ea.getElements()[0].text = text;
ea.getElements()[0].rawText = text;
await ea.addElementsToView(false,false);
return;
}
ea.addText(0,0,text);
await ea.addElementsToView(true, false, true);

View File

@@ -36,4 +36,4 @@ el.y = size[1];
el.width = size[2];
el.height = size[3];
ea.copyViewElementsToEAforEditing([el]);
ea.addElementsToView();
ea.addElementsToView(false,false);

View File

@@ -15,4 +15,4 @@ font = parseInt(await utils.suggester(font,["1","2","3"]));
if (isNaN(font)) return;
elements.forEach((el)=>el.fontFamily = font);
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
ea.addElementsToView(false,false);

View File

@@ -50,4 +50,4 @@ for(el of elements) { //doing for instead of .forEach due to await inputPrompt
};
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
ea.addElementsToView(false,false);

View File

@@ -13,4 +13,4 @@ width = await utils.inputPrompt("Width?","number",width);
const elements=ea.getViewSelectedElements();
ea.copyViewElementsToEAforEditing(elements);
ea.getElements().forEach((el)=>el.strokeWidth=width);
ea.addElementsToView();
ea.addElementsToView(false,false);

View File

@@ -14,4 +14,4 @@ let align = ["left","right","center"];
align = await utils.suggester(align,align);
elements.forEach((el)=>el.textAlign = align);
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
ea.addElementsToView(false,false);

View File

@@ -68,7 +68,7 @@ elements.forEach((el)=>{
elementsToMove.push({fillId: newEl.id, shapeId: el.id});
});
await ea.addElementsToView();
await ea.addElementsToView(false,false);
elementsToMove.forEach((x)=>{
const viewElements = ea.getViewElements();
ea.moveViewElementToZIndex(

View File

@@ -23,5 +23,5 @@ elements.forEach((el)=>{
ea.addText(el.x,el.y+i*el.height/text.length,text[i]);
}
});
ea.addElementsToView();
ea.addElementsToView(false,false);
ea.deleteViewElements(elements);

View File

@@ -0,0 +1,49 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/ea-toggle-fullscreen.jpg)
Hides Obsidian workspace leaf padding and header (based on option in settings, default is "hide header" = true) which will take Excalidraw to full screen. ⚠ Note that if the header is not visible, it will be very difficult to invoke the command palette to end full screen. Only hide the header if you have a keyboard or you've practiced opening command palette!
```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();
if(!settings["Hide header"]) {
settings = {
"Hide header": {
value: false,
},
...settings
};
await ea.setScriptSettings(settings);
}
if(!settings["Hide header"].description) {
settings["Hide header"].description = "⚠ Note that if the header is not visible, it will be very difficult to invoke the command palette to end full screen. Only hide the header if you have a keyboard or you've practiced opening command palette!";
await ea.setScriptSettings(settings);
}
const hideHeader = settings["Hide header"].value;
const newStylesheet = document.createElement("style");
newStylesheet.id = "excalidraw-full-screen";
newStylesheet.textContent = `
.workspace-leaf-content .view-content {
padding: 0px !important;
}
${hideHeader?`
.view-header {
height: 1px !important;
}`:""}
.status-bar {
display: none !important;
}
`;
const oldStylesheet = document.getElementById(newStylesheet.id);
if(oldStylesheet) document.head.removeChild(oldStylesheet);
else document.head.appendChild(newStylesheet);

View File

@@ -53,7 +53,10 @@ I would love to include your contribution in the script library. If you have a s
- [[#Modify background color opacity]]
- [[#Normalize Selected Arrows]]
- [[#OCR - Optical Character Recognition]]
- [[#Organic Line]]
- [[#Repeat Elements]]
- [[#Reverse arrows]]
- [[#Scribble Helper]]
- [[#Select Elements of Type]]
- [[#Set background color of unclosed line object by adding a shadow clone]]
- [[#Set Dimensions]]
@@ -63,6 +66,7 @@ I would love to include your contribution in the script library. If you have a s
- [[#Set Stroke Width of Selected Elements]]
- [[#Set Text Alignment]]
- [[#Split text by lines]]
- [[#Toggle Fullscreen on Mobile]]
- [[#Transfer TextElements to Excalidraw markdown metadata]]
- [[#Zoom to Fit Selected Elements]]
@@ -234,12 +238,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/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/OCR%20-%20Optical%20Character%20Recognition.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">REQUIRES EXCALIDRAW 1.5.15<br>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.<br><mark>⚠ Note that you will need to manually paste your token into the script after the first run! ⚠</mark><br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-ocr.jpg'><br><iframe width="560" height="315" src="https://www.youtube.com/embed/W2NMzR8s4eE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></td></tr></table>
## Organic Line
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Organic%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/Organic%20Line.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Converts selected freedraw lines such that pencil pressure will decrease from maximum to minimum from the beginning of the line to its end. The resulting line is placed at the back of the layers, under all other items. Helpful when drawing organic mindmaps.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-organic-line.jpg'></td></tr></table>
## Repeat Elements
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Repeat%20Elements.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/Repeat%20Elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will detect the difference between 2 selected elements, including position, size, angle, stroke and background color, and create several elements that repeat these differences based on the number of repetitions entered by the user.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-repeat-elements.png'></td></tr></table>
## Reverse arrows
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Reverse%20arrows.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/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>
## Scribble Helper
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Scribble%20Helper.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/Scribble%20Helper.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">iOS scribble helper for better handwriting experience with text elements. If no elements are selected then the creates a text element at pointer position and you can use the edit box to modify the text with scribble. If a text element is selected then opens the input prompt where you can modify this text with scribble.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-scribble-helper.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
@@ -294,6 +316,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/Split%20text%20by%20lines.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Split lines of text into separate text elements for easier reorganization<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-split-lines.jpg'></td></tr></table>
## Toggle Fullscreen on Mobile
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Toggle%20Fullscreen%20on%20Mobile.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/Toggle%20Fullscreen%20on%20Mobile.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Hides Obsidian workspace leaf padding and header (based on option in settings, default is "hide header" = false) which will take Excalidraw to full screen. ⚠ Note that if the header is not visible, it will be very difficult to invoke the command palette to end full screen. Only hide the header if you have a keyboard or you've practiced opening command palette!<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/ea-toggle-fullscreen.jpg'></td></tr></table>
## Transfer TextElements to Excalidraw markdown metadata
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

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

View File

@@ -1,23 +1,25 @@
{
"name": "obsidian-excalidraw-plugin",
"version": "1.3.21",
"version": "1.6.13",
"description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings",
"main": "main.js",
"scripts": {
"dev": "cross-env NODE_ENV=development rollup --config rollup.config.js -w",
"build": "cross-env NODE_ENV=production rollup --config rollup.config.js",
"build": "cross-env NODE_ENV=production rollup --config rollup.config.js && terser main.js --compress toplevel=true,passes=2 --output main.js",
"code:fix": "eslint --max-warnings=0 --ext .ts,.tsx ./src --fix"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@zsviczian/excalidraw": "0.10.0-obsidian-45",
"@zsviczian/excalidraw": "0.11.0-obsidian-1",
"monkey-around": "^2.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "^5.0.0",
"roughjs": "^4.5.2"
"roughjs": "^4.5.2",
"lz-string": "^1.4.4",
"@types/lz-string": "^1.3.34"
},
"devDependencies": {
"@babel/core": "^7.16.12",
@@ -38,6 +40,7 @@
"obsidian": "^0.13.21",
"rollup": "^2.66.0",
"rollup-plugin-visualizer": "^5.5.4",
"rollup-plugin-terser": "^7.0.2",
"tslib": "^2.3.1",
"typescript": "^4.5.5",
"eslint-config-prettier": "8.3.0",

View File

@@ -5,6 +5,7 @@ import { env } from "process";
import babel from '@rollup/plugin-babel';
import replace from "@rollup/plugin-replace";
import visualizer from "rollup-plugin-visualizer";
import { terser } from "rollup-plugin-terser";
const isProd = (process.env.NODE_ENV === "production");
console.log("Is production", isProd);
@@ -30,5 +31,8 @@ export default {
nodeResolve({ browser: true, preferBuiltins: true }),
typescript({inlineSources: !isProd}),
visualizer(),
...isProd ? [
terser({toplevel: true, compress: {passes: 2}})
] : []
],
};

View File

@@ -53,6 +53,7 @@ export class EmbeddedFile {
public mimeType: MimeType = "application/octet-stream";
public size: Size = { height: 0, width: 0 };
public linkParts: LinkParts;
/*public isHyperlink: boolean = false;*/
constructor(plugin: ExcalidrawPlugin, hostPath: string, imgPath: string) {
this.plugin = plugin;
@@ -60,6 +61,12 @@ export class EmbeddedFile {
}
public resetImage(hostPath: string, imgPath: string) {
/*if(imgPath.startsWith("https://") || imgPath.startsWith("http://")) {
this.img=imgPath;
this.imgInverted=imgPath;
this.isHyperlink = true;
return;
}*/
this.imgInverted = this.img = "";
this.mtime = 0;
this.linkParts = getLinkParts(imgPath);
@@ -133,6 +140,9 @@ export class EmbeddedFile {
}
public getImage(isDark: boolean) {
/*if(this.isHyperlink) {
return this.img;
}*/
if (!this.file) {
return "";
}

View File

@@ -15,6 +15,7 @@ import {
VIEW_TYPE_EXCALIDRAW,
MAX_IMAGE_SIZE,
PLUGIN_ID,
COLOR_NAMES,
} from "./constants";
import {
//debug,
@@ -36,7 +37,6 @@ import {
getMaximumGroups,
intersectElementWithLine,
} from "@zsviczian/excalidraw";
import { stringify } from "querystring";
declare type ConnectionPoint = "top" | "bottom" | "left" | "right" | null;
const GAP = 4;
@@ -234,6 +234,11 @@ export interface ExcalidrawAutomate {
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
hexStringToRgb(color: string):number[];
rgbToHexString(color: number[]):string;
hslToRgb(color: number[]):number[];
rgbToHsl(color:number[]):number[];
colorNameToHex(color:string):string;
}
declare let window: any;
@@ -1142,10 +1147,11 @@ export async function initExcalidrawAutomate(
copyViewElementsToEAforEditing(elements: ExcalidrawElement[]): void {
elements.forEach((el) => {
this.elementsDict[el.id] = {
version: el.version + 1,
...el,
};
});
version: el.version + 1,
updated: Date.now(),
versionNonce: Math.floor(Math.random()*1000000000),
};});
},
viewToggleFullScreen(forceViewMode: boolean = false): void {
if (this.plugin.app.isMobile) {
@@ -1167,14 +1173,9 @@ export async function initExcalidrawAutomate(
commitToHistory: false,
});
}
if (
document.fullscreenElement ===
(this.targetView as ExcalidrawView).contentEl
) {
document.exitFullscreen();
} else {
(this.targetView as ExcalidrawView).contentEl.requestFullscreen();
}
const view = this.targetView as ExcalidrawView;
if(view.isFullscreen()) view.exitFullscreen();
else view.gotoFullscreen();
},
connectObjectWithViewSelectedElement(
objectA: string,
@@ -1352,6 +1353,108 @@ export async function initExcalidrawAutomate(
elements: elements,
commitToHistory: true,
});
},
hexStringToRgb(color: string):number[] {
const res = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
return [parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16)];
},
rgbToHexString(color: number[]):string {
const colorInt =
((Math.round(color[0]) & 0xff) << 16) +
((Math.round(color[1]) & 0xff) << 8) +
(Math.round(color[2]) & 0xff);
const colorStr = colorInt.toString(16).toLowerCase();
return "#"+"000000".substring(colorStr.length) + colorStr;
},
hslToRgb(color: number[]):number[] {
const h = color[0] / 360;
const s = color[1] / 100;
const l = color[2] / 100;
let t2;
let t3;
let val;
if (s === 0) {
val = l * 255;
return [val, val, val];
}
if (l < 0.5) {
t2 = l * (1 + s);
} else {
t2 = l + s - l * s;
}
const t1 = 2 * l - t2;
const rgb = [0, 0, 0];
for (let i = 0; i < 3; i++) {
t3 = h + (1 / 3) * -(i - 1);
if (t3 < 0) {
t3++;
}
if (t3 > 1) {
t3--;
}
if (6 * t3 < 1) {
val = t1 + (t2 - t1) * 6 * t3;
} else if (2 * t3 < 1) {
val = t2;
} else if (3 * t3 < 2) {
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
} else {
val = t1;
}
rgb[i] = val * 255;
}
return rgb;
},
rgbToHsl(color:number[]):number[] {
const r = color[0] / 255;
const g = color[1] / 255;
const b = color[2] / 255;
const min = Math.min(r, g, b);
const max = Math.max(r, g, b);
const delta = max - min;
let h;
let s;
if (max === min) {
h = 0;
} else if (r === max) {
h = (g - b) / delta;
} else if (g === max) {
h = 2 + (b - r) / delta;
} else if (b === max) {
h = 4 + (r - g) / delta;
}
h = Math.min(h * 60, 360);
if (h < 0) {
h += 360;
}
const l = (min + max) / 2;
if (max === min) {
s = 0;
} else if (l <= 0.5) {
s = delta / (max + min);
} else {
s = delta / (2 - max - min);
}
return [h, s * 100, l * 100];
},
colorNameToHex(color:string):string {
if (COLOR_NAMES.has(color.toLowerCase().trim())) {
return COLOR_NAMES.get(color.toLowerCase().trim());
}
return color.trim();
}
};
await initFonts();
@@ -1401,7 +1504,8 @@ function boxedElement(
strokeSharpness: ea.style.strokeSharpness,
seed: Math.floor(Math.random() * 100000),
version: 1,
versionNonce: 1,
versionNonce: Math.floor(Math.random()*1000000000),
updated: Date.now(),
isDeleted: false,
groupIds: [] as any,
boundElements: [] as any,
@@ -1443,6 +1547,9 @@ export function measureText(
fontSize: number,
fontFamily: number,
) {
//following odd error with mindmap on iPad while synchornizing with desktop.
if(!fontSize) fontSize = 20;
if(!fontFamily) fontFamily = 1;
const line = document.createElement("div");
const body = document.body;
line.style.position = "absolute";

View File

@@ -20,7 +20,10 @@ import ExcalidrawPlugin from "./main";
import { JSON_parse } from "./constants";
import { TextMode } from "./ExcalidrawView";
import {
compress,
decompress,
getAttachmentsFolderAndFilePath,
//getBakPath,
getBinaryFileFromDataURL,
getLinkParts,
isObsidianThemeDark,
@@ -80,13 +83,66 @@ export const REGEX_LINK = {
},
};
export const REG_LINKINDEX_HYPERLINK = /^\w+:\/\//;
//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;
const DRAWING_COMPRESSED_REG = /(\n# Drawing\n[^`]*(?:```compressed\-json\n))([\s\S]*?)(```\n)/gm; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/182
const DRAWING_COMPRESSED_REG_FALLBACK = /(\n# Drawing\n(?:```compressed\-json\n)?)(.*)((```)?(%%)?)/gm;
export const REG_LINKINDEX_HYPERLINK = /^\w+:\/\//;
const isCompressedMD = (data:string):boolean => {
return data.match(/```compressed\-json\n/gm) !== null;
}
const getDecompressedScene = (data:string):[string,IteratorResult<RegExpMatchArray, any>] => {
let res = data.matchAll(DRAWING_COMPRESSED_REG);
//In case the user adds a text element with the contents "# Drawing\n"
let parts;
parts = res.next();
if (parts.done) {
//did not find a match
res = data.matchAll(DRAWING_COMPRESSED_REG_FALLBACK);
parts = res.next();
}
if (parts.value && parts.value.length > 1) {
return [decompress(parts.value[2]),parts];
}
return [null,parts];
}
export const changeThemeOfExcalidrawMD = (data:string):string => {
const compressed = isCompressedMD(data);
let scene = compressed ? getDecompressedScene(data)[0] : data;
if(!scene) return data;
if(isObsidianThemeDark) {
if((scene.match(/"theme"\s*:\s*"light"\s*,/g)||[]).length === 1) {
scene = scene.replace(/"theme"\s*:\s*"light"\s*,/,`"theme": "dark",`);
}
} else {
if((scene.match(/"theme"\s*:\s*"dark"\s*,/g)||[]).length === 1) {
scene = scene.replace(/"theme"\s*:\s*"dark"\s*,/,`"theme": "light",`);
}
}
if(compressed) {
return data.replace(DRAWING_COMPRESSED_REG,`$1${compress(scene)}$3`);
}
return scene;
}
export function getJSON(data: string): { scene: string; pos: number } {
let res = data.matchAll(DRAWING_REG);
let res;
if(isCompressedMD(data)) {
const [result,parts] = getDecompressedScene(data);
if(result) {
return {
scene: result.substring(0, result.lastIndexOf("}") + 1),
pos: parts.value.index,
}; //this is a workaround in case sync merges two files together and one version is still an old version without the ```codeblock
}
return { scene: data, pos: parts.value ? parts.value.index : 0 };
}
res = data.matchAll(DRAWING_REG);
//In case the user adds a text element with the contents "# Drawing\n"
let parts;
@@ -106,8 +162,10 @@ export function getJSON(data: string): { scene: string; pos: number } {
return { scene: data, pos: parts.value ? parts.value.index : 0 };
}
export function getMarkdownDrawingSection(jsonString: string) {
return `%%\n# Drawing\n\x60\x60\x60json\n${jsonString}\n\x60\x60\x60\n%%`;
export function getMarkdownDrawingSection(jsonString: string, compressed: boolean) {
return compressed
? `%%\n# Drawing\n\x60\x60\x60compressed-json\n${compress(jsonString)}\n\x60\x60\x60\n%%`
: `%%\n# Drawing\n\x60\x60\x60json\n${jsonString}\n\x60\x60\x60\n%%`;
}
/**
@@ -264,18 +322,17 @@ export class ExcalidrawData {
}
return sceneJSONandPOS;
};
try {
//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);
/*} catch (e) {
if(await this.app.vault.adapter.exists(getBakPath(file))) {
data = await this.app.vault.adapter.read(getBakPath(file))
sceneJSONandPOS = loadJSON();
new Notice(t("LOAD_FROM_BACKUP"), 4000);
} else {
throw e;
}
}
}*/
if (!this.scene.files) {
this.scene.files = {}; //loading legacy scenes that do not yet have the files attribute.
@@ -372,12 +429,14 @@ export class ExcalidrawData {
if (!file) {
return false;
}
this.loaded = false;
this.compatibilityMode = true;
this.file = file;
this.textElements = new Map<
string,
{ raw: string; parsed: string; wrapAt: number }
>();
this.elementLinks = new Map<string, string>();
this.setShowLinkBrackets();
this.setLinkPrefix();
this.setUrlPrefix();
@@ -394,6 +453,7 @@ export class ExcalidrawData {
this.findNewTextElementsInScene();
this.findNewElementLinksInScene();
await this.setTextMode(TextMode.raw, true); //legacy files are always displayed in raw mode.
this.loaded = true;
return true;
}
@@ -762,6 +822,7 @@ export class ExcalidrawData {
* Generate markdown file representation of excalidraw drawing
* @returns markdown string
*/
disableCompression: boolean = false;
generateMD(): string {
let outString = "# Text Elements\n";
for (const key of this.textElements.keys()) {
@@ -789,7 +850,10 @@ export class ExcalidrawData {
outString += this.equations.size > 0 || this.files.size > 0 ? "\n" : "";
const sceneJSONstring = JSON.stringify(this.scene, null, "\t");
return outString + getMarkdownDrawingSection(sceneJSONstring);
return outString + getMarkdownDrawingSection(
sceneJSONstring,
this.disableCompression ? false: this.plugin.settings.compress
);
}
/**
@@ -909,7 +973,7 @@ export class ExcalidrawData {
let result = false;
if (!this.compatibilityMode) {
result = await this.syncFiles();
this.scene.files = {};
this.scene.files = {}; //files contains the dataURLs of files. Once synced these are all saved to disk
}
this.updateElementLinksFromScene();
result =

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
import { App, FuzzySuggestModal, TFile } from "obsidian";
import { IMAGE_TYPES } from "./constants";
import { fileURLToPath } from "url";
import { IMAGE_TYPES, REG_LINKINDEX_INVALIDCHARS } from "./constants";
import ExcalidrawView from "./ExcalidrawView";
import { t } from "./lang/helpers";
import ExcalidrawPlugin from "./main";
@@ -27,7 +28,9 @@ export class InsertImageDialog extends FuzzySuggestModal<TFile> {
getItems(): TFile[] {
return (this.app.vault.getFiles() || []).filter(
(f: TFile) =>
IMAGE_TYPES.contains(f.extension) || this.plugin.isExcalidrawFile(f),
(IMAGE_TYPES.contains(f.extension) || this.plugin.isExcalidrawFile(f)) &&
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/422
!f.path.match(REG_LINKINDEX_INVALIDCHARS),
);
}
@@ -42,7 +45,7 @@ export class InsertImageDialog extends FuzzySuggestModal<TFile> {
ea.canvas.theme = this.view.excalidrawAPI.getAppState().theme;
(async () => {
await ea.addImage(0, 0, item);
ea.addElementsToView(true, false);
ea.addElementsToView(true, false, true);
})();
}

View File

@@ -1,4 +1,5 @@
import { App, FuzzySuggestModal, TFile } from "obsidian";
import { REG_LINKINDEX_INVALIDCHARS } from "./constants";
import { t } from "./lang/helpers";
export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
@@ -21,8 +22,9 @@ export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
}
getItems(): any[] {
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/422
//@ts-ignore
return this.app.metadataCache.getLinkSuggestions();
return this.app.metadataCache.getLinkSuggestions().filter(x=>!x.path.match(REG_LINKINDEX_INVALIDCHARS));
}
getItemText(item: any): string {

View File

@@ -39,7 +39,7 @@ export class InsertMDDialog extends FuzzySuggestModal<TFile> {
ea.setView(this.view);
(async () => {
await ea.addImage(0, 0, item);
ea.addElementsToView(true, false);
ea.addElementsToView(true, false, true);
})();
}

View File

@@ -29,6 +29,12 @@ let plugin: ExcalidrawPlugin;
let vault: Vault;
let metadataCache: MetadataCache;
const getDefaultWidth = (plugin: ExcalidrawPlugin):string => {
const width = parseInt(plugin.settings.width);
if(isNaN(width) || width === 0) return "400";
return plugin.settings.width;
}
export const initializeMarkdownPostProcessor = (p: ExcalidrawPlugin) => {
plugin = p;
vault = p.app.vault;
@@ -218,7 +224,7 @@ const processInternalEmbeds = async (
if (file && file instanceof TFile && plugin.isExcalidrawFile(file)) {
attr.fwidth = maybeDrawing.getAttribute("width")
? maybeDrawing.getAttribute("width")
: plugin.settings.width;
: getDefaultWidth(plugin);
attr.fheight = maybeDrawing.getAttribute("height");
alt = maybeDrawing.getAttribute("alt");
if (alt == attr.fname) {
@@ -232,9 +238,9 @@ const processInternalEmbeds = async (
if (maybeDrawing.tagName.toLowerCase() == "span") {
alt = `|${alt}`;
}
//1:width, 2:height, 3:style 1 2 3
//1:width, 2:height, 3:style 1 2 3
parts = alt.match(/[^\|]*\|?(\d*%?)x?(\d*%?)\|?(.*)/);
attr.fwidth = parts[1] ? parts[1] : plugin.settings.width;
attr.fwidth = parts[1] ? parts[1] : getDefaultWidth(plugin);
attr.fheight = parts[2];
if (parts[3] != attr.fname) {
attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`;
@@ -269,7 +275,7 @@ const tmpObsidianWYSIWYG = async (
const attr: imgElementAttributes = {
fname: ctx.sourcePath,
fheight: "",
fwidth: plugin.settings.width,
fwidth: getDefaultWidth(plugin),
style: "excalidraw-svg",
};
@@ -328,7 +334,7 @@ const tmpObsidianWYSIWYG = async (
if (hasAttr) {
//1:width, 2:height, 3:style 1 2 3
const parts = alt.match(/(\d*%?)x?(\d*%?)\|?(.*)/);
attr.fwidth = parts[1] ? parts[1] : plugin.settings.width;
attr.fwidth = parts[1] ? parts[1] : getDefaultWidth(plugin);
attr.fheight = parts[2];
if (parts[3] != attr.fname) {
attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`;
@@ -336,7 +342,7 @@ const tmpObsidianWYSIWYG = async (
}
if (!hasWidth && !hasHeight && !hasAttr) {
attr.fheight = "";
attr.fwidth = plugin.settings.width;
attr.fwidth = getDefaultWidth(plugin);
attr.style = "excalidraw-svg";
}
};

View File

@@ -7,6 +7,7 @@ import {
FuzzySuggestModal,
Instruction,
TFile,
Notice,
} from "obsidian";
import ExcalidrawView from "./ExcalidrawView";
import ExcalidrawPlugin from "./main";
@@ -405,21 +406,42 @@ export class NewFileActions extends Modal {
//files manually follow one of two options:
el.style.textAlign = "right";
const checks = ():boolean => {
if(!this.path || this.path === "") {
new Notice ("Error: Filename for new file may not be empty");
return false;
}
if(!this.view.file) {
new Notice ("Unknown error. It seems as if your drawing was closed or the drawing file is missing");
return false;
}
return true;
}
const createFile = async (data:string):Promise<TFile> => {
if(!this.path.includes("/")) {
const re = new RegExp(`${this.view.file.name}$`,"g");
this.path = this.view.file.path.replace(re,this.path)
}
if(!this.path.match(/\.md$/)) {
this.path = this.path+".md";
}
const f = await this.app.vault.create(this.path,data)
return f;
}
const bMd = el.createEl("button", { text: "Create Markdown" });
bMd.onclick = async () => {
//@ts-ignore
const f = await this.app.fileManager.createNewMarkdownFileFromLinktext(this.path,this.viewFile);
if(!checks) return;
const f = await createFile("");
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());
if(!checks) return;
const f = await createFile(await this.plugin.getBlankDrawing());
await sleep(200); //wait for metadata cache to update, so file opens as excalidraw
this.openFile(f);
this.close();

View File

@@ -446,6 +446,36 @@ export const EXCALIDRAW_AUTOMATE_INFO:SuggestorInfo[] = [
desc: "Moves the element to a specific position in the z-index",
after: "",
},
{
field: "hexStringToRgb",
code: "hexStringToRgb(color: string):number[];",
desc: "Converts a HEX color to an RGB number array. #FF0000 to [255,0,0]",
after: "",
},
{
field: "rgbToHexString",
code: "rgbToHexString(color: number[]):string;",
desc: "Converts an RGB number array to a HEX string. [255,0,0] to #FF0000",
after: "",
},
{
field: "hslToRgb",
code: "hslToRgb(color: number[]):number[];",
desc: "Converts an HSL number array to an RGB number array. [0,100,50] to [255,0,0]",
after: "",
},
{
field: "rgbToHsl",
code: "rgbToHsl(color:number[]):number[];",
desc: "Converts an RGB number array to an HSL number array. [255,0,0] to [0,100,50]",
after: "",
},
{
field: "colorNameToHex",
code: "colorNameToHex(color:string):string;",
desc: "Converts a CSS color name to its HEX color equivalent. 'White' to #FFFFFF",
after: "",
},
];
export const EXCALIDRAW_SCRIPTENGINE_INFO:SuggestorInfo[] = [
@@ -467,49 +497,49 @@ export const EXCALIDRAW_SCRIPTENGINE_INFO:SuggestorInfo[] = [
export const FRONTMATTER_KEYS_INFO:SuggestorInfo[] = [
{
field: FRONTMATTER_KEY,
field: "plugin",
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,
field: "link-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,
field: "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,
field: "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,
field: "default-mode",
code: null,
desc: "Specifies how Excalidraw should open by default. Valid values are: view|zen",
after: ": view",
},
{
field: FRONTMATTER_KEY_FONT,
field: "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,
field: "font-color",
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,
field: "css",
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

@@ -5,6 +5,7 @@ import {
Notice,
request,
TAbstractFile,
TFile,
TFolder,
Vault,
WorkspaceLeaf,
@@ -20,6 +21,7 @@ import {
import ExcalidrawPlugin from "./main";
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/element/types";
import { ExportSettings } from "./ExcalidrawView";
import { compressToBase64, decompressFromBase64 } from "lz-string";
declare module "obsidian" {
interface Workspace {
@@ -82,9 +84,9 @@ export function splitFolderAndFilename(filepath: string): {
basename: string;
} {
const lastIndex = filepath.lastIndexOf("/");
const filename = lastIndex == -1 ? filepath : filepath.substr(lastIndex + 1);
const filename = lastIndex == -1 ? filepath : filepath.substring(lastIndex + 1);
return {
folderpath: normalizePath(filepath.substr(0, lastIndex)),
folderpath: normalizePath(filepath.substring(0, lastIndex)),
filename,
basename: filename.replace(/\.[^/.]+$/, ""),
};
@@ -124,6 +126,11 @@ export function getIMGPathFromExcalidrawFile(
);
}
/*export function getBakPath(file:TFile):string {
const re = new RegExp(`${file.name}$`,"g");
return file.path.replace(re,`.${file.name}.bak`);
}*/
/**
* Create new file, if file already exists find first unique filename by adding a number to the end of the filename
* @param filename
@@ -544,6 +551,14 @@ export const getLinkParts = (fname: string): LinkParts => {
};
};
export const compress = (data:string):string => {
return compressToBase64(data).replace(/(.{1024})/g, "$1\n");
};
export const decompress = (data:string):string => {
return decompressFromBase64(data.replaceAll("\n","").replaceAll("\r",""));
};
export const errorlog = (data: {}) => {
console.error({ plugin: "Excalidraw", ...data });
};

View File

@@ -16,7 +16,7 @@ export const PLUGIN_ID = "obsidian-excalidraw-plugin";
export const SCRIPT_INSTALL_CODEBLOCK = "excalidraw-script-install";
export const SCRIPT_INSTALL_FOLDER = "Downloaded";
export const fileid = customAlphabet("1234567890abcdef", 40);
export const REG_LINKINDEX_INVALIDCHARS = /[<>:"\\|?*]/g;
export const REG_LINKINDEX_INVALIDCHARS = /[<>:"\\|?*#]/g;
export const REG_BLOCK_REF_CLEAN =
/\+|\/|~|=|%|\(|\)|{|}|,|\.|\$|!|\?|;|\[|]|\^|#|\*|<|>|&|@|\||\\|"|:/g;
export const IMAGE_TYPES = ["jpeg", "jpg", "png", "gif", "svg"];
@@ -55,6 +55,148 @@ export const TEXT_DISPLAY_RAW_ICON_NAME = "presentation";
export const FULLSCREEN_ICON_NAME = "fullscreen";
export const EXIT_FULLSCREEN_ICON_NAME = "exit-fullscreen";
export const SCRIPTENGINE_ICON_NAME = "ScriptEngine";
export const COLOR_NAMES = new Map<string,string>();
COLOR_NAMES.set("aliceblue","#f0f8ff");
COLOR_NAMES.set("antiquewhite","#faebd7");
COLOR_NAMES.set("aqua","#00ffff");
COLOR_NAMES.set("aquamarine","#7fffd4");
COLOR_NAMES.set("azure","#f0ffff");
COLOR_NAMES.set("beige","#f5f5dc");
COLOR_NAMES.set("bisque","#ffe4c4");
COLOR_NAMES.set("black","#000000");
COLOR_NAMES.set("blanchedalmond","#ffebcd");
COLOR_NAMES.set("blue","#0000ff");
COLOR_NAMES.set("blueviolet","#8a2be2");
COLOR_NAMES.set("brown","#a52a2a");
COLOR_NAMES.set("burlywood","#deb887");
COLOR_NAMES.set("cadetblue","#5f9ea0");
COLOR_NAMES.set("chartreuse","#7fff00");
COLOR_NAMES.set("chocolate","#d2691e");
COLOR_NAMES.set("coral","#ff7f50");
COLOR_NAMES.set("cornflowerblue","#6495ed");
COLOR_NAMES.set("cornsilk","#fff8dc");
COLOR_NAMES.set("crimson","#dc143c");
COLOR_NAMES.set("cyan","#00ffff");
COLOR_NAMES.set("darkblue","#00008b");
COLOR_NAMES.set("darkcyan","#008b8b");
COLOR_NAMES.set("darkgoldenrod","#b8860b");
COLOR_NAMES.set("darkgray","#a9a9a9");
COLOR_NAMES.set("darkgreen","#006400");
COLOR_NAMES.set("darkkhaki","#bdb76b");
COLOR_NAMES.set("darkmagenta","#8b008b");
COLOR_NAMES.set("darkolivegreen","#556b2f");
COLOR_NAMES.set("darkorange","#ff8c00");
COLOR_NAMES.set("darkorchid","#9932cc");
COLOR_NAMES.set("darkred","#8b0000");
COLOR_NAMES.set("darksalmon","#e9967a");
COLOR_NAMES.set("darkseagreen","#8fbc8f");
COLOR_NAMES.set("darkslateblue","#483d8b");
COLOR_NAMES.set("darkslategray","#2f4f4f");
COLOR_NAMES.set("darkturquoise","#00ced1");
COLOR_NAMES.set("darkviolet","#9400d3");
COLOR_NAMES.set("deeppink","#ff1493");
COLOR_NAMES.set("deepskyblue","#00bfff");
COLOR_NAMES.set("dimgray","#696969");
COLOR_NAMES.set("dodgerblue","#1e90ff");
COLOR_NAMES.set("firebrick","#b22222");
COLOR_NAMES.set("floralwhite","#fffaf0");
COLOR_NAMES.set("forestgreen","#228b22");
COLOR_NAMES.set("fuchsia","#ff00ff");
COLOR_NAMES.set("gainsboro","#dcdcdc");
COLOR_NAMES.set("ghostwhite","#f8f8ff");
COLOR_NAMES.set("gold","#ffd700");
COLOR_NAMES.set("goldenrod","#daa520");
COLOR_NAMES.set("gray","#808080");
COLOR_NAMES.set("green","#008000");
COLOR_NAMES.set("greenyellow","#adff2f");
COLOR_NAMES.set("honeydew","#f0fff0");
COLOR_NAMES.set("hotpink","#ff69b4");
COLOR_NAMES.set("indianred","#cd5c5c");
COLOR_NAMES.set("indigo","#4b0082");
COLOR_NAMES.set("ivory","#fffff0");
COLOR_NAMES.set("khaki","#f0e68c");
COLOR_NAMES.set("lavender","#e6e6fa");
COLOR_NAMES.set("lavenderblush","#fff0f5");
COLOR_NAMES.set("lawngreen","#7cfc00");
COLOR_NAMES.set("lemonchiffon","#fffacd");
COLOR_NAMES.set("lightblue","#add8e6");
COLOR_NAMES.set("lightcoral","#f08080");
COLOR_NAMES.set("lightcyan","#e0ffff");
COLOR_NAMES.set("lightgoldenrodyellow","#fafad2");
COLOR_NAMES.set("lightgrey","#d3d3d3");
COLOR_NAMES.set("lightgreen","#90ee90");
COLOR_NAMES.set("lightpink","#ffb6c1");
COLOR_NAMES.set("lightsalmon","#ffa07a");
COLOR_NAMES.set("lightseagreen","#20b2aa");
COLOR_NAMES.set("lightskyblue","#87cefa");
COLOR_NAMES.set("lightslategray","#778899");
COLOR_NAMES.set("lightsteelblue","#b0c4de");
COLOR_NAMES.set("lightyellow","#ffffe0");
COLOR_NAMES.set("lime","#00ff00");
COLOR_NAMES.set("limegreen","#32cd32");
COLOR_NAMES.set("linen","#faf0e6");
COLOR_NAMES.set("magenta","#ff00ff");
COLOR_NAMES.set("maroon","#800000");
COLOR_NAMES.set("mediumaquamarine","#66cdaa");
COLOR_NAMES.set("mediumblue","#0000cd");
COLOR_NAMES.set("mediumorchid","#ba55d3");
COLOR_NAMES.set("mediumpurple","#9370d8");
COLOR_NAMES.set("mediumseagreen","#3cb371");
COLOR_NAMES.set("mediumslateblue","#7b68ee");
COLOR_NAMES.set("mediumspringgreen","#00fa9a");
COLOR_NAMES.set("mediumturquoise","#48d1cc");
COLOR_NAMES.set("mediumvioletred","#c71585");
COLOR_NAMES.set("midnightblue","#191970");
COLOR_NAMES.set("mintcream","#f5fffa");
COLOR_NAMES.set("mistyrose","#ffe4e1");
COLOR_NAMES.set("moccasin","#ffe4b5");
COLOR_NAMES.set("navajowhite","#ffdead");
COLOR_NAMES.set("navy","#000080");
COLOR_NAMES.set("oldlace","#fdf5e6");
COLOR_NAMES.set("olive","#808000");
COLOR_NAMES.set("olivedrab","#6b8e23");
COLOR_NAMES.set("orange","#ffa500");
COLOR_NAMES.set("orangered","#ff4500");
COLOR_NAMES.set("orchid","#da70d6");
COLOR_NAMES.set("palegoldenrod","#eee8aa");
COLOR_NAMES.set("palegreen","#98fb98");
COLOR_NAMES.set("paleturquoise","#afeeee");
COLOR_NAMES.set("palevioletred","#d87093");
COLOR_NAMES.set("papayawhip","#ffefd5");
COLOR_NAMES.set("peachpuff","#ffdab9");
COLOR_NAMES.set("peru","#cd853f");
COLOR_NAMES.set("pink","#ffc0cb");
COLOR_NAMES.set("plum","#dda0dd");
COLOR_NAMES.set("powderblue","#b0e0e6");
COLOR_NAMES.set("purple","#800080");
COLOR_NAMES.set("rebeccapurple","#663399");
COLOR_NAMES.set("red","#ff0000");
COLOR_NAMES.set("rosybrown","#bc8f8f");
COLOR_NAMES.set("royalblue","#4169e1");
COLOR_NAMES.set("saddlebrown","#8b4513");
COLOR_NAMES.set("salmon","#fa8072");
COLOR_NAMES.set("sandybrown","#f4a460");
COLOR_NAMES.set("seagreen","#2e8b57");
COLOR_NAMES.set("seashell","#fff5ee");
COLOR_NAMES.set("sienna","#a0522d");
COLOR_NAMES.set("silver","#c0c0c0");
COLOR_NAMES.set("skyblue","#87ceeb");
COLOR_NAMES.set("slateblue","#6a5acd");
COLOR_NAMES.set("slategray","#708090");
COLOR_NAMES.set("snow","#fffafa");
COLOR_NAMES.set("springgreen","#00ff7f");
COLOR_NAMES.set("steelblue","#4682b4");
COLOR_NAMES.set("tan","#d2b48c");
COLOR_NAMES.set("teal","#008080");
COLOR_NAMES.set("thistle","#d8bfd8");
COLOR_NAMES.set("tomato","#ff6347");
COLOR_NAMES.set("turquoise","#40e0d0");
COLOR_NAMES.set("violet","#ee82ee");
COLOR_NAMES.set("wheat","#f5deb3");
COLOR_NAMES.set("white","#ffffff");
COLOR_NAMES.set("whitesmoke","#f5f5f5");
COLOR_NAMES.set("yellow","#ffff00");
COLOR_NAMES.set("yellowgreen","#9acd32");
export const SCRIPTENGINE_ICON = `<g transform="translate(-8,-8)"><path d="M24.318 37.983c-1.234-1.232-8.433-3.903-7.401-7.387 1.057-3.484 9.893-12.443 13.669-13.517 3.776-1.074 6.142 6.523 9.012 7.073 2.87.55 6.797-1.572 8.207-3.694 1.384-2.148-3.147-7.413.15-9.168 3.298-1.755 16.389-2.646 19.611-1.284 3.247 1.363-1.611 7.335-.151 9.483 1.46 2.148 6.067 3.746 8.836 3.38 2.769-.368 4.154-6.733 7.728-5.633 3.575 1.1 12.36 8.828 13.67 12.233 1.308 3.406-5.186 5.423-5.79 8.2-.58 2.75-.026 6.705 2.265 8.355 2.266 1.65 9.642-1.78 11.404 1.598 1.762 3.38 1.007 15.35-.806 18.651-1.787 3.353-7.753-.367-9.969 1.31-2.215 1.65-3.901 5.92-3.373 8.67.504 2.777 7.754 4.48 6.445 7.885C96.49 87.543 87.15 95.454 83.5 96.685c-3.65 1.231-4.96-4.741-7.577-5.16-2.593-.393-6.57.707-8.03 2.75-1.436 2.017 2.668 7.806-.63 9.483-3.323 1.676-15.759 2.226-19.157.655-3.373-1.598.554-7.964-1.108-10.138-1.687-2.174-6.394-3.431-9.012-2.907-2.643.55-3.273 7.282-6.747 6.103-3.499-1.126-12.788-9.535-14.172-13.019-1.36-3.484 5.437-5.108 5.966-7.858.529-2.777-.68-7.073-2.744-8.697-2.064-1.624-7.93 2.41-9.642-1.126-1.737-3.537-2.441-16.765-.654-20.118 1.787-3.3 9.062 1.598 11.429.183 2.366-1.44 2.316-7.282 2.769-8.749m.126-.104c-1.234-1.232-8.433-3.903-7.401-7.387 1.057-3.484 9.893-12.443 13.669-13.517 3.776-1.074 6.142 6.523 9.012 7.073 2.87.55 6.797-1.572 8.207-3.694 1.384-2.148-3.147-7.413.15-9.168 3.298-1.755 16.389-2.646 19.611-1.284 3.247 1.363-1.611 7.335-.151 9.483 1.46 2.148 6.067 3.746 8.836 3.38 2.769-.368 4.154-6.733 7.728-5.633 3.575 1.1 12.36 8.828 13.67 12.233 1.308 3.406-5.186 5.423-5.79 8.2-.58 2.75-.026 6.705 2.265 8.355 2.266 1.65 9.642-1.78 11.404 1.598 1.762 3.38 1.007 15.35-.806 18.651-1.787 3.353-7.753-.367-9.969 1.31-2.215 1.65-3.901 5.92-3.373 8.67.504 2.777 7.754 4.48 6.445 7.885C96.49 87.543 87.15 95.454 83.5 96.685c-3.65 1.231-4.96-4.741-7.577-5.16-2.593-.393-6.57.707-8.03 2.75-1.436 2.017 2.668 7.806-.63 9.483-3.323 1.676-15.759 2.226-19.157.655-3.373-1.598.554-7.964-1.108-10.138-1.687-2.174-6.394-3.431-9.012-2.907-2.643.55-3.273 7.282-6.747 6.103-3.499-1.126-12.788-9.535-14.172-13.019-1.36-3.484 5.437-5.108 5.966-7.858.529-2.777-.68-7.073-2.744-8.697-2.064-1.624-7.93 2.41-9.642-1.126-1.737-3.537-2.441-16.765-.654-20.118 1.787-3.3 9.062 1.598 11.429.183 2.366-1.44 2.316-7.282 2.769-8.749" fill="none" stroke-width="2" stroke-linecap="round" stroke="currentColor"/><path d="M81.235 56.502a23.3 23.3 0 0 1-1.46 8.068 20.785 20.785 0 0 1-1.762 3.72 24.068 24.068 0 0 1-5.337 6.26 22.575 22.575 0 0 1-3.449 2.358 23.726 23.726 0 0 1-7.803 2.803 24.719 24.719 0 0 1-8.333 0 24.102 24.102 0 0 1-4.028-1.074 23.71 23.71 0 0 1-3.776-1.729 23.259 23.259 0 0 1-6.369-5.265 23.775 23.775 0 0 1-2.416-3.353 24.935 24.935 0 0 1-1.762-3.72 23.765 23.765 0 0 1-1.083-3.981 23.454 23.454 0 0 1 0-8.173c.252-1.336.604-2.698 1.083-3.956a24.935 24.935 0 0 1 1.762-3.72 22.587 22.587 0 0 1 2.416-3.378c.881-1.048 1.888-2.017 2.946-2.908a24.38 24.38 0 0 1 3.423-2.357 23.71 23.71 0 0 1 3.776-1.73 21.74 21.74 0 0 1 4.028-1.047 23.437 23.437 0 0 1 8.333 0 24.282 24.282 0 0 1 7.803 2.777 26.198 26.198 0 0 1 3.45 2.357 24.62 24.62 0 0 1 5.336 6.287 20.785 20.785 0 0 1 1.762 3.72 21.32 21.32 0 0 1 1.083 3.955c.251 1.336.302 3.405.377 4.086.05.681.05-.68 0 0" fill="none" stroke-width="4" stroke-linecap="round" stroke="currentColor"/><path d="M69.404 56.633c-6.596-3.3-13.216-6.6-19.51-9.744m19.51 9.744c-6.747-3.379-13.493-6.758-19.51-9.744m0 0v19.489m0-19.49v19.49m0 0c4.355-2.148 8.71-4.322 19.51-9.745m-19.51 9.745c3.978-1.965 7.93-3.956 19.51-9.745m0 0h0m0 0h0" fill="currentColor" stroke-linecap="round" stroke="currentColor" stroke-width="4"/></g>`;
export const DISK_ICON_NAME = "disk";
export const DISK_ICON = `<path fill="none" stroke="currentColor" fill="#fff" d="M0 0h100v100H0z"/><path fill="none" stroke="currentColor" d="M20.832 4.168c21.824.145 43.645.289 74.68.5m-74.68-.5c17.09.113 34.176.227 74.68.5m0 0c.094 27.3.191 54.602.32 91.164m-.32-91.164c.113 32.633.23 65.27.32 91.164m0 0H4.168m91.664 0H4.168m0 0v-75m0 75v-75m0 0L20.832 4.168M4.168 20.832L20.832 4.168M20.832 4.168h58.336m-58.336 0h58.336m0 0v25m0-25v25m0 0H20.832m58.336 0H20.832m0 0v-25m0 25v-25" stroke-width="1.66668" /><path fill="none" stroke="currentColor" d="M29.168 4.168h16.664v16.664H29.168"/><path fill="none" stroke="currentColor" d="M29.168 4.168h16.664m-16.664 0h16.664m0 0v16.664m0-16.664v16.664m0 0H29.168m16.664 0H29.168m0 0V4.168m0 16.664V4.168M12.5 54.168h75m-75 0h75m0 0v41.664m0-41.664v41.664m0 0h-75m75 0h-75m0 0V54.168m0 41.664V54.168M20.832 62.5c20.11-.18 40.219-.36 55.68-.5m-55.68.5c14.656-.133 29.313-.262 55.68-.5M20.832 71.332c13.098-.117 26.2-.234 55.68-.5m-55.68.5l55.68-.5M21.117 79.582c20.645-.184 41.285-.371 55.68-.5m-55.68.5c18.153-.16 36.301-.324 55.68-.5" stroke-width="1.66668"/>`;

View File

@@ -44,6 +44,8 @@ export default {
INSERT_LATEX:
"Insert LaTeX formula (e.g. \\binom{n}{k} = \\frac{n!}{k!(n-k)!})",
ENTER_LATEX: "Enter a valid LaTeX expression",
TRAY_MODE: "Toggle property-panel tray-mode",
SEARCH: "Search for text in drawing",
//ExcalidrawView.ts
INSTALL_SCRIPT_BUTTON: "Install or update Excalidraw Scripts",
@@ -59,11 +61,11 @@ export default {
TEXT_ELEMENT_EMPTY:
"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 name cannot contain any of the following characters: * " \\ < > : | ? #',
FILE_DOES_NOT_EXIST:
"File does not exist. Hold down ALT (or ALT+SHIFT) and CLICK link button to create a new file.",
FORCE_SAVE:
"Force-save to update transclusions in adjacent panes.\n(Please note, that autosave is always on)",
"Force-save to update transclusions in adjacent panes.\n(Check autosave settings in plugin settings.)",
RAW: "Change to PREVIEW mode (only effects text-elements with links or transclusions)",
PARSED:
"Change to RAW mode (only effects text-elements with links or transclusions)",
@@ -95,12 +97,23 @@ export default {
"You can access your scripts from Excalidraw via the Obsidian Command Palette. Assign " +
"hotkeys to your favorite scripts just like to any other Obsidian command. " +
"The folder may not be the root folder of your Vault. ",
AUTOSAVE_NAME: "Autosave",
COMPRESS_NAME: "Compress Excalidraw JSON in Markdown",
COMPRESS_DESC: "By enabling this feature Excalidraw will store the drawing JSON in a Base64 compressed " +
"format using the <a href='https://pieroxy.net/blog/pages/lz-string/index.html'>LZ-String</a> algorithm. " +
"This will reduce the chance of Excalidraw JSON cluttering your search results in Obsidian. " +
"As a side effect, this will also reduce the filesize of Excalidraw drawings. " +
"When you switch an Excalidraw drawing to Markdown view, using the options menu in Excalidraw, the file will " +
"be saved without compression, so that you can read and edit the JSON string. The drawing will be compressed again " +
"once you switch back to Excalidraw view. " +
"The setting only has effect 'point forward', meaning, existing drawings will not be effected by the setting " +
"until you open them and save them. ",
AUTOSAVE_NAME: "Enable Autosave",
AUTOSAVE_DESC:
"Automatically save the active drawing every 30 seconds. Save normally happens when you close Excalidraw or Obsidian, or move " +
"focus to another pane. In rare cases autosave may slightly disrupt your drawing flow. I created this feature with mobile " +
"phones in mind (I only have experience with Android), where 'swiping out Obsidian to close it' led to some data loss, and because " +
"I wasn't able to force save on application termination on mobiles. If you use Excalidraw on a desktop this is likely not needed.",
"Automatically save the active drawing, in case there are changes, every 15, 30 seconds, or 1, 2, 3, 4, or 5 minute. Save normally happens when you close Excalidraw or Obsidian, or move " +
"focus to another pane. I created this feature with mobile " +
"phones and tablets in mind, where 'swiping out Obsidian to close it' led to some data loss.",
AUTOSAVE_INTERVAL_NAME: "Interval for autosave",
AUTOSAVE_INTERVAL_DESC: "The time interval between saves. Autosave will skip if there are no changes in the drawing.",
FILENAME_HEAD: "Filename",
FILENAME_DESC:
"<p>The auto-generated filename consists of a prefix and a date. " +
@@ -173,6 +186,8 @@ export default {
}${FRONTMATTER_KEY_CUSTOM_URL_PREFIX}: "🌐 "</code> to the file's frontmatter.`,
HOVERPREVIEW_NAME: "Hover preview without CTRL/CMD key",
HOVERPREVIEW_DESC: "Toggle On: Hover preview for [[wiki links]] is shown immedately, without the need to hold the CTRL/CMD key.<br>Toggle Off: Hover preview is shown only when you hold the CTRL/CMD key while hovering the link.",
LINKOPACITY_NAME: "Opacity of link icon",
LINKOPACITY_DESC: "Opacity of the link indicator icon in the top right corner of an element. 1 is opaque, 0 is transparent.",
LINK_CTRL_CLICK_NAME:
"CTRL/CMD + CLICK on text with [[links]] or [](links) to open them",
LINK_CTRL_CLICK_DESC:
@@ -233,7 +248,7 @@ export default {
"When Obsidian is in light mode, Excalidraw will render light mode as well. You may want to switch 'Export image with background' off for a more Obsidian-integrated look and feel.",
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 " +
"The default width of an embedded drawing. This applies to live preview edit and reading mode, as well as to hover previews. You can specify a custom " +
"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",
@@ -242,6 +257,8 @@ export default {
"or a PNG or an SVG copy. You need to enable auto-export PNG / SVG (see below under Export Settings) for those image types to be available in the dropdown. For drawings that do not have a " +
"a corresponding PNG or SVG readily available the command palette action will insert a broken link. You need to open the original drawing and initiate export manually. " +
"This option will not autogenerate PNG/SVG files, but will simply reference the already existing files.",
EMBED_WIKILINK_NAME: "Embed SVG or PNG as Wiki link",
EMBED_WIKILINK_DESC: "Toggle ON: Excalidraw will embed a [[wiki link]]. Toggle OFF: Excalidraw will embed a [markdown](link).",
EXPORT_PNG_SCALE_NAME: "PNG export image scale",
EXPORT_PNG_SCALE_DESC: "The size-scale of the exported PNG image",
EXPORT_BACKGROUND_NAME: "Export image with background",

View File

@@ -15,6 +15,8 @@ import {
loadMathJax,
Scope,
request,
MetadataCache,
FrontMatterCache,
} from "obsidian";
import {
BLANK_DRAWING,
@@ -42,7 +44,7 @@ import {
VIRGIL_DATAURL,
} from "./constants";
import ExcalidrawView, { TextMode } from "./ExcalidrawView";
import { getMarkdownDrawingSection } from "./ExcalidrawData";
import { changeThemeOfExcalidrawMD, getMarkdownDrawingSection } from "./ExcalidrawData";
import {
ExcalidrawSettings,
DEFAULT_SETTINGS,
@@ -97,6 +99,7 @@ declare module "obsidian" {
}
export default class ExcalidrawPlugin extends Plugin {
private excalidrawFiles: Set<TFile> = new Set<TFile>();
public excalidrawFileModes: { [file: string]: string } = {};
private _loaded: boolean = false;
public settings: ExcalidrawSettings;
@@ -757,6 +760,55 @@ export default class ExcalidrawPlugin extends Plugin {
},
});
this.addCommand({
id: "search-text",
name: t("SEARCH"),
checkCallback: (checking: boolean) => {
if (checking) {
return (
this.app.workspace.activeLeaf.view.getViewType() ===
VIEW_TYPE_EXCALIDRAW
);
}
const view = this.app.workspace.activeLeaf.view;
if (view instanceof ExcalidrawView) {
(async ()=>{
const ea = this.ea;
ea.reset();
ea.setView(view);
const elements = ea.getViewElements().filter(el=>el.type==="text");
if(elements.length === 0) return;
let text = await ScriptEngine.inputPrompt(this.app,"Search for","use quotation marks for exact match","");
if(!text) return;
const res = text.matchAll(/"(.*?)"/g)
let query:string[] = [];
let parts;
while (!(parts = res.next()).done) {
query.push(parts.value[1]);
}
text = text.replaceAll(/"(.*?)"/g, "");
query = query.concat(text.split(" ").filter(s=>s.length!==0));
const match = elements
.filter((el:any)=>query
.some(q=>el
.rawText
.toLowerCase()
.replaceAll("\n"," ")
.match(q.toLowerCase())
));
if(match.length === 0) {
new Notice("I could not find a matching text element");
return;
}
ea.selectElementsInView(match);
ea.getExcalidrawAPI().zoomToFit(match,this.settings.zoomToFitMaxLevel,0.05);
})();
return true;
}
return false;
},
});
this.addCommand({
id: "export-png",
name: t("EXPORT_PNG"),
@@ -873,6 +925,32 @@ export default class ExcalidrawPlugin extends Plugin {
},
});
this.addCommand({
id: "tray-mode",
name: t("TRAY_MODE"),
checkCallback: (checking: boolean) => {
if (checking) {
const view = this.app.workspace.activeLeaf.view;
return view instanceof ExcalidrawView;
}
const view = this.app.workspace.activeLeaf.view;
if (view instanceof ExcalidrawView && view.excalidrawAPI) {
const st = view.excalidrawAPI.getAppState();
st.trayModeEnabled = !st.trayModeEnabled;
view.excalidrawAPI.updateScene({appState:st});
//placed in an async function because I need to load settings first
//just in case settings were updated via sync
(async()=>{
await this.loadSettings();
this.settings.defaultTrayMode = st.trayModeEnabled;
this.saveSettings();
})();
return true;
}
return false;
},
});
this.addCommand({
id: "insert-md",
name: t("INSERT_MD"),
@@ -916,7 +994,7 @@ export default class ExcalidrawPlugin extends Plugin {
ea.reset();
await ea.addLaTex(0, 0, formula);
ea.setView(view);
ea.addElementsToView(true, false);
ea.addElementsToView(true, false, true);
});
return true;
}
@@ -1227,14 +1305,9 @@ export default class ExcalidrawPlugin extends Plugin {
if (!(file instanceof TFile)) {
return;
}
const isExcalidarwFile =
//@ts-ignore
(file.unsafeCachedData &&
//@ts-ignore
file.unsafeCachedData.search(
/---[\r\n]+[\s\S]*excalidraw-plugin:\s*\w+[\r\n]+[\s\S]*---/gm,
) > -1) ||
file.extension == "excalidraw";
const isExcalidarwFile = this.excalidrawFiles.has(file);
this.updateFileCache(file,undefined,true);
if (!isExcalidarwFile) {
return;
}
@@ -1269,13 +1342,14 @@ export default class ExcalidrawPlugin extends Plugin {
self.registerEvent(self.app.vault.on("delete", deleteEventHandler));
//save open drawings when user quits the application
const quitEventHandler = async () => {
//Removing because it is not guaranteed to run, and frequently gets terminated mid flight, causing file consistency issues
/*const quitEventHandler = async () => {
const leaves = self.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
for (let i = 0; i < leaves.length; i++) {
await (leaves[i].view as ExcalidrawView).save(true);
}
};
self.registerEvent(self.app.workspace.on("quit", quitEventHandler));
self.registerEvent(self.app.workspace.on("quit", quitEventHandler));*/
//save Excalidraw leaf and update embeds when switching to another leaf
const activeLeafChangeEventHandler = async (leaf: WorkspaceLeaf) => {
@@ -1346,9 +1420,36 @@ export default class ExcalidrawPlugin extends Plugin {
activeLeafChangeEventHandler,
),
);
const metaCache:MetadataCache = self.app.metadataCache;
//@ts-ignore
metaCache.getCachedFiles().forEach((filename:string) => {
const fm = metaCache.getCache(filename)?.frontmatter;
if ((fm && Object.keys(fm).contains(FRONTMATTER_KEY)) ||
filename.match(/\.excalidraw$/)
) {
self.updateFileCache(
self.app.vault.getAbstractFileByPath(filename) as TFile, fm
);
}
});
this.registerEvent(metaCache.on("changed", (file, data, cache) => this.updateFileCache(file, cache?.frontmatter)));
});
}
updateFileCache(file: TFile, frontmatter?: FrontMatterCache, deleted: boolean = false) {
if(frontmatter) {
const isExcalidrawFile = Object.keys(frontmatter).contains(FRONTMATTER_KEY);
this.excalidrawFiles.add(file);
return;
}
if(!deleted && file.extension==="excalidraw") {
this.excalidrawFiles.add(file);
return;
}
this.excalidrawFiles.delete(file);
}
onunload() {
window.removeEventListener("keydown", this.onKeyDown, false);
window.removeEventListener("keyup", this.onKeyUp, false);
@@ -1386,7 +1487,8 @@ export default class ExcalidrawPlugin extends Plugin {
)
const editor = activeView.editor;
if (this.settings.embedType === "excalidraw") {
editor.replaceSelection(`![[${data}]]`);
editor.replaceSelection(this.settings.embedWikiLink
? `![[${data}]]` : `![](${encodeURI(data)})`);
editor.focus();
return;
}
@@ -1398,11 +1500,15 @@ export default class ExcalidrawPlugin extends Plugin {
file.path,"."+this.settings.embedType.toLowerCase()
);
await this.app.vault.create(filepath, "");
//await sleep(200);
const imgFile = this.app.vault.getAbstractFileByPath(filepath);
if(!imgFile) {
await this.app.vault.create(filepath, "");
await sleep(200);
}
editor.replaceSelection(
`![[${filename}]]\n%%[[${data}|🖋 Edit in Excalidraw]]%%`,
this.settings.embedWikiLink
? `![[${filename}]]\n%%[[${data}|🖋 Edit in Excalidraw]]%%`
: `![](${encodeURI(filename)})\n%%[🖋 Edit in Excalidraw](${encodeURI(data)})%%`,
);
editor.focus();
}
@@ -1486,9 +1592,9 @@ export default class ExcalidrawPlugin extends Plugin {
(template.extension == "md" && !this.settings.compatibilityMode) ||
(template.extension == "excalidraw" && this.settings.compatibilityMode)
) {
const data = await this.app.vault.read(template);
let data = await this.app.vault.read(template);
if (data) {
return data;
return this.settings.matchTheme ? changeThemeOfExcalidrawMD(data) : data;
}
}
}
@@ -1501,7 +1607,7 @@ export default class ExcalidrawPlugin extends Plugin {
this.settings.matchTheme && isObsidianThemeDark()
? DARK_BLANK_DRAWING
: BLANK_DRAWING;
return `${FRONTMATTER}\n${getMarkdownDrawingSection(blank)}`;
return `${FRONTMATTER}\n${getMarkdownDrawingSection(blank,this.settings.compress)}`;
}
/**
@@ -1532,7 +1638,7 @@ export default class ExcalidrawPlugin extends Plugin {
}
return (
outString +
getMarkdownDrawingSection(JSON.stringify(JSON_parse(data), null, "\t"))
getMarkdownDrawingSection(JSON.stringify(JSON_parse(data), null, "\t"),this.settings.compress)
);
}
@@ -1596,4 +1702,5 @@ export default class ExcalidrawPlugin extends Plugin {
const fileCache = f ? this.app.metadataCache.getFileCache(f) : null;
return !!fileCache?.frontmatter && !!fileCache.frontmatter[FRONTMATTER_KEY];
}
}

View File

@@ -16,6 +16,9 @@ export interface ExcalidrawSettings {
embedUseExcalidrawFolder: boolean;
templateFilePath: string;
scriptFolderPath: string;
compress: boolean;
autosave: boolean;
autosaveInterval: number;
drawingFilenamePrefix: string;
drawingEmbedPrefixWithFilename: boolean;
drawingFilenameDateTime: string;
@@ -33,6 +36,7 @@ export interface ExcalidrawSettings {
linkPrefix: string;
urlPrefix: string;
hoverPreviewWithoutCTRL: boolean;
linkOpacity: number;
allowCtrlClick: boolean; //if disabled only the link button in the view header will open links
forceWrap: boolean;
pageTransclusionCharLimit: number;
@@ -46,6 +50,7 @@ export interface ExcalidrawSettings {
autoexportPNG: boolean;
autoexportExcalidraw: boolean;
embedType: "excalidraw" | "PNG" | "SVG";
embedWikiLink: boolean,
syncExcalidraw: boolean;
compatibilityMode: boolean;
experimentalFileType: boolean;
@@ -68,6 +73,7 @@ export interface ExcalidrawSettings {
mdFontColor: string;
mdCSS: string;
scriptEngineSettings: {};
defaultTrayMode: boolean;
}
export const DEFAULT_SETTINGS: ExcalidrawSettings = {
@@ -75,6 +81,9 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
embedUseExcalidrawFolder: false,
templateFilePath: "Excalidraw/Template.excalidraw",
scriptFolderPath: "Excalidraw/Scripts",
compress: false,
autosave: true,
autosaveInterval: 15000,
drawingFilenamePrefix: "Drawing ",
drawingEmbedPrefixWithFilename: true,
drawingFilenameDateTime: "YYYY-MM-DD HH.mm.ss",
@@ -90,6 +99,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
linkPrefix: "📍",
urlPrefix: "🌐",
hoverPreviewWithoutCTRL: false,
linkOpacity: 1,
openInAdjacentPane: false,
showLinkBrackets: true,
allowCtrlClick: true,
@@ -105,6 +115,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
autoexportPNG: false,
autoexportExcalidraw: false,
embedType: "excalidraw",
embedWikiLink: true,
syncExcalidraw: false,
experimentalFileType: false,
experimentalFileTag: "✏️",
@@ -132,6 +143,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
mdFontColor: "Black",
mdCSS: "",
scriptEngineSettings: {},
defaultTrayMode: false,
};
const fragWithHTML = (html: string) =>
@@ -253,7 +265,19 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}),
);
this.containerEl.createEl("h1", { text: t("FILENAME_HEAD") });
new Setting(containerEl)
.setName(t("COMPRESS_NAME"))
.setDesc(fragWithHTML(t("COMPRESS_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.compress)
.onChange(async (value) => {
this.plugin.settings.compress = value;
this.applySettingsUpdate();
}),
);
this.containerEl.createEl("h1", { text: t("FILENAME_HEAD") });
containerEl.createDiv("", (el) => {
el.innerHTML = t("FILENAME_DESC");
});
@@ -317,6 +341,40 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}),
);
let autosaveDropdown: DropdownComponent;
new Setting(containerEl)
.setName(t("AUTOSAVE_NAME"))
.setDesc(fragWithHTML(t("AUTOSAVE_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.autosave)
.onChange(async (value) => {
this.plugin.settings.autosave = value;
autosaveDropdown.setDisabled(!value);
this.applySettingsUpdate();
}),
);
new Setting(containerEl)
.setName(t("AUTOSAVE_INTERVAL_NAME"))
.setDesc(fragWithHTML(t("AUTOSAVE_INTERVAL_DESC")))
.addDropdown(async (d: DropdownComponent) => {
autosaveDropdown = d;
d.addOption("15000", "15 seconds");
d.addOption("30000", "30 seconds");
d.addOption("60000", "1 minute");
d.addOption("120000", "2 minutes");
d.addOption("180000", "3 minutes");
d.addOption("240000", "4 minutes");
d.addOption("300000", "5 minutes");
d.setValue(this.plugin.settings.autosaveInterval.toString())
.onChange((value) => {
this.plugin.settings.autosaveInterval = parseInt(value);
this.applySettingsUpdate(true);
});
});
this.containerEl.createEl("h1", { text: t("DISPLAY_HEAD") });
new Setting(containerEl)
@@ -460,6 +518,27 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
this.applySettingsUpdate(true);
}),
);
let opacityText: HTMLDivElement;
new Setting(containerEl)
.setName(t("LINKOPACITY_NAME"))
.setDesc(fragWithHTML(t("LINKOPACITY_DESC")))
.addSlider((slider) =>
slider
.setLimits(0, 1, 0.05)
.setValue(this.plugin.settings.linkOpacity)
.onChange(async (value) => {
opacityText.innerText = ` ${value.toString()}`;
this.plugin.settings.linkOpacity = value;
this.applySettingsUpdate(true);
}),
)
.settingEl.createDiv("", (el) => {
opacityText = el;
el.style.minWidth = "2.3em";
el.style.textAlign = "right";
el.innerText = ` ${this.plugin.settings.linkOpacity.toString()}`;
});
new Setting(containerEl)
.setName(t("HOVERPREVIEW_NAME"))
@@ -712,6 +791,18 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
});
});
new Setting(containerEl)
.setName(t("EMBED_WIKILINK_NAME"))
.setDesc(fragWithHTML(t("EMBED_WIKILINK_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.embedWikiLink)
.onChange(async (value) => {
this.plugin.settings.embedWikiLink = value;
this.applySettingsUpdate();
}),
);
let scaleText: HTMLDivElement;
new Setting(containerEl)

View File

@@ -118,7 +118,7 @@ li[data-testid] {
}
.excalidraw-scriptengine-install td.label {
width: 11ch;
min-width: 11ch;
font-weight: bold;
padding-right: 5px;
}
@@ -144,4 +144,12 @@ li[data-testid] {
text-align: center;
font-weight: bold;
margin-bottom: 2em;
}
.excalidraw-dirty {
color: red;
}
.workspace-leaf-content .excalidraw-view {
padding: 0px 1px; /*1px so on ipad swipe in from left and right still works*/
}

View File

@@ -3,7 +3,7 @@
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"module": "es2015",
"target": "es2017",
"allowJs": true,
"noImplicitAny": true,

View File

@@ -1,4 +1,4 @@
{
"1.6.1": "0.12.16",
"1.6.14": "0.12.16",
"1.4.2": "0.11.13"
}

View File

@@ -1748,6 +1748,11 @@
"resolved" "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
"version" "0.0.29"
"@types/lz-string@^1.3.34":
"integrity" "sha512-j6G1e8DULJx3ONf6NdR5JiR2ZY3K3PaaqiEuKYkLQO0Czfi1AzrtjfnfCROyWGeDd5IVMKCwsgSmMip9OWijow=="
"resolved" "https://registry.npmjs.org/@types/lz-string/-/lz-string-1.3.34.tgz"
"version" "1.3.34"
"@types/mime@^1":
"integrity" "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
"resolved" "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz"
@@ -2127,10 +2132,10 @@
"resolved" "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz"
"version" "4.2.2"
"@zsviczian/excalidraw@0.10.0-obsidian-45":
"integrity" "sha512-m0RaRTg1jgeDBZCGmYmvKo0GDvGioBZGI+HBamcFGs2ZqN8RYmnetuS6U2RZSqTmKlgr/++1VXqi5vrGkTSzQw=="
"resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.10.0-obsidian-45.tgz"
"version" "0.10.0-obsidian-45"
"@zsviczian/excalidraw@0.11.0-obsidian-1":
"integrity" "sha512-jW2vCnNvk8/0QDBEhnVZ2yImHXkbUISCTdb+KG3yWhU5rwn8nH6tQPJGxHfnr5dN5D5TPrguh9ScqdezoKOzUw=="
"resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.11.0-obsidian-1.tgz"
"version" "0.11.0-obsidian-1"
dependencies:
"dotenv" "10.0.0"
@@ -5866,6 +5871,11 @@
dependencies:
"yallist" "^4.0.0"
"lz-string@^1.4.4":
"integrity" "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY="
"resolved" "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz"
"version" "1.4.4"
"magic-string@^0.25.0", "magic-string@^0.25.7":
"integrity" "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA=="
"resolved" "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz"
@@ -7482,7 +7492,7 @@
dependencies:
"glob" "^7.1.3"
"rollup-plugin-terser@^7.0.0":
"rollup-plugin-terser@^7.0.0", "rollup-plugin-terser@^7.0.2":
"integrity" "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ=="
"resolved" "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz"
"version" "7.0.2"