mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ca87741a5 | ||
|
|
463eb1d228 | ||
|
|
935febf337 | ||
|
|
e4c7fe7fc9 | ||
|
|
e98b1755ee | ||
|
|
bc71bde9b7 | ||
|
|
2dc10af004 | ||
|
|
46d21f53e5 | ||
|
|
44d9b44c88 | ||
|
|
5875f268f4 | ||
|
|
bc1120ac3a | ||
|
|
40a10620c1 | ||
|
|
28295deaa0 | ||
|
|
b783d9b992 | ||
|
|
98df940f52 | ||
|
|
ab3b84a765 | ||
|
|
5fc6905ac7 | ||
|
|
00c7a0e934 | ||
|
|
60ed7f21f6 | ||
|
|
9e0c5c48d4 | ||
|
|
7e76c2b693 | ||
|
|
dd9b363427 | ||
|
|
90b693151a | ||
|
|
d280c7e953 | ||
|
|
b227ad05fd | ||
|
|
cf07458fbb | ||
|
|
c191b7264d | ||
|
|
109fe05302 | ||
|
|
d55bb1a2c4 | ||
|
|
1362c7f416 |
14
.github/ISSUE_TEMPLATE/bug_report.md
vendored
14
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -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.
|
||||
|
||||
@@ -14,7 +14,7 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
|
||||
|[](https://youtu.be/r08wk-58DPk)|[](https://youtu.be/tsecSfnTMow)|[](https://youtu.be/K6qZkTz8GHs)|
|
||||
|[](https://youtu.be/hePJcObHIso)|[](https://youtu.be/NOuddK6xrr8)|[](https://youtu.be/lzYdOQ6z8F0)|
|
||||
|[](https://youtu.be/eKFmrSQhFA4)|[](https://youtu.be/qbPIAZguJeo)|[](https://youtu.be/2Y8OhkGiTHg)|
|
||||
|[](https://youtu.be/2v9TZmQNO8c)|||
|
||||
|[](https://youtu.be/2v9TZmQNO8c)|[](https://youtu.be/xHPGWR3m0c8)||
|
||||
|
||||
|
||||
# Key features
|
||||
@@ -28,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -22,4 +22,4 @@ elements.forEach((el)=>{
|
||||
);
|
||||
ea.addToGroup([el.id,ellipseId]);
|
||||
});
|
||||
ea.addElementsToView();
|
||||
ea.addElementsToView(false,false);
|
||||
@@ -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)]);
|
||||
@@ -132,7 +132,7 @@ for(const elements of groups) {
|
||||
ea.addToGroup([id].concat(elements.map((el)=>el.id)));
|
||||
}
|
||||
|
||||
await ea.addElementsToView(false);
|
||||
await ea.addElementsToView(false,false);
|
||||
|
||||
function recalculateStartPointOfLine(line, el) {
|
||||
const aX = el.x + el.width/2;
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -78,4 +78,4 @@ ea.connectObjects(
|
||||
numberOfPoints: linePoints
|
||||
}
|
||||
);
|
||||
ea.addElementsToView(false,true,true);
|
||||
ea.addElementsToView(false,false,true);
|
||||
@@ -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());
|
||||
@@ -48,4 +48,4 @@ elements.forEach((el)=>{
|
||||
el.containerId = id;
|
||||
});
|
||||
ea.copyViewElementsToEAforEditing(elements);
|
||||
ea.addElementsToView();
|
||||
ea.addElementsToView(false,false);
|
||||
@@ -53,4 +53,4 @@ for (const line of lines) {
|
||||
}
|
||||
|
||||
ea.copyViewElementsToEAforEditing(lines);
|
||||
await ea.addElementsToView();
|
||||
await ea.addElementsToView(false,false);
|
||||
|
||||
@@ -25,7 +25,7 @@ for(const arrow of selectedIndividualArrows) {
|
||||
}
|
||||
|
||||
ea.copyViewElementsToEAforEditing(selectedIndividualArrows);
|
||||
await ea.addElementsToView();
|
||||
await ea.addElementsToView(false,false);
|
||||
|
||||
function recalculateStartPointOfLine(line, el, elB) {
|
||||
const aX = el.x + el.width/2;
|
||||
|
||||
27
ea-scripts/Organic Line.md
Normal file
27
ea-scripts/Organic Line.md
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||

|
||||
|
||||
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));
|
||||
@@ -42,6 +42,8 @@ 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.||[@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.||[@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.||[@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.||[@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.||[@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.||[@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.||[@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.||[@zsviczian](https://github.com/zsviczian)|
|
||||
@@ -53,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.||[@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||[@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.||[@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!||[@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.||[@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)|
|
||||
|
||||
378
ea-scripts/Repeat Elements.md
Normal file
378
ea-scripts/Repeat Elements.md
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
|
||||

|
||||
|
||||
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
|
||||
|
||||

|
||||
|
||||
This script will 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;
|
||||
}
|
||||
@@ -20,4 +20,4 @@ elements.forEach((el)=>{
|
||||
el.endArrowhead = start;
|
||||
});
|
||||
ea.copyViewElementsToEAforEditing(elements);
|
||||
ea.addElementsToView();
|
||||
ea.addElementsToView(false,false);
|
||||
@@ -21,9 +21,9 @@ if(elements.length === 1) {
|
||||
ea.getElements()[0].originalText = text;
|
||||
ea.getElements()[0].text = text;
|
||||
ea.getElements()[0].rawText = text;
|
||||
await ea.addElementsToView(false,true);
|
||||
await ea.addElementsToView(false,false);
|
||||
return;
|
||||
}
|
||||
|
||||
ea.addText(0,0,text);
|
||||
await ea.addElementsToView(true, true, true);
|
||||
await ea.addElementsToView(true, false, true);
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
49
ea-scripts/Toggle Fullscreen on Mobile.md
Normal file
49
ea-scripts/Toggle Fullscreen on Mobile.md
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||

|
||||
|
||||
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);
|
||||
@@ -53,6 +53,8 @@ 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]]
|
||||
@@ -64,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]]
|
||||
|
||||
@@ -235,6 +238,18 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
|
||||
```
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/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
|
||||
@@ -301,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
|
||||
|
||||
BIN
images/ea-toggle-fullscreen.jpg
Normal file
BIN
images/ea-toggle-fullscreen.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
BIN
images/scripts-organic-line.jpg
Normal file
BIN
images/scripts-organic-line.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
BIN
images/scripts-repeat-elements.png
Normal file
BIN
images/scripts-repeat-elements.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 156 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.6.11",
|
||||
"version": "1.6.14",
|
||||
"minAppVersion": "0.12.16",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsidian-excalidraw-plugin",
|
||||
"version": "1.6.9",
|
||||
"version": "1.6.13",
|
||||
"description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
@@ -12,12 +12,14 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zsviczian/excalidraw": "0.10.0-obsidian-49",
|
||||
"@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",
|
||||
|
||||
@@ -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;
|
||||
@@ -1168,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,
|
||||
@@ -1353,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();
|
||||
@@ -1445,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";
|
||||
|
||||
@@ -20,8 +20,10 @@ import ExcalidrawPlugin from "./main";
|
||||
import { JSON_parse } from "./constants";
|
||||
import { TextMode } from "./ExcalidrawView";
|
||||
import {
|
||||
compress,
|
||||
decompress,
|
||||
getAttachmentsFolderAndFilePath,
|
||||
getBakPath,
|
||||
//getBakPath,
|
||||
getBinaryFileFromDataURL,
|
||||
getLinkParts,
|
||||
isObsidianThemeDark,
|
||||
@@ -81,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;
|
||||
@@ -107,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%%`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,17 +322,17 @@ export class ExcalidrawData {
|
||||
}
|
||||
return sceneJSONandPOS;
|
||||
};
|
||||
try {
|
||||
//try {
|
||||
sceneJSONandPOS = loadJSON();
|
||||
} catch (e) {
|
||||
/*} 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 =
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
WorkspaceItem,
|
||||
Notice,
|
||||
Menu,
|
||||
MarkdownView,
|
||||
} from "obsidian";
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
@@ -53,7 +54,7 @@ import {
|
||||
download,
|
||||
embedFontsInSVG,
|
||||
errorlog,
|
||||
getBakPath,
|
||||
//getBakPath,
|
||||
getIMGFilename,
|
||||
getNewOrAdjacentLeaf,
|
||||
getNewUniqueFilepath,
|
||||
@@ -145,6 +146,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
public excalidrawAPI: any = null;
|
||||
public excalidrawWrapperRef: React.MutableRefObject<any> = null;
|
||||
private justLoaded: boolean = false;
|
||||
private preventAutozoomOnLoad: boolean = false;
|
||||
private plugin: ExcalidrawPlugin;
|
||||
private dirty: string = null;
|
||||
public autosaveTimer: any = null;
|
||||
@@ -258,7 +260,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
//- by monkeypatches on detach(next)
|
||||
//This semaphore helps avoid collision of saves
|
||||
private preventSave:boolean = false; //this.saving is taken by Obsidian. When I set this, nothing got saved at all.
|
||||
async save(preventReload: boolean = true) {
|
||||
async save(preventReload: boolean = true, forcesave: boolean = false) {
|
||||
if(this.preventSave) {
|
||||
return;
|
||||
}
|
||||
@@ -278,6 +280,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
|
||||
this.preventReload = preventReload;
|
||||
const allowSave = (this.dirty !== null && this.dirty) || this.autosaving || forcesave; //dirty == false when view.file == null;
|
||||
this.dirty = null;
|
||||
const scene = this.getScene();
|
||||
|
||||
@@ -287,12 +290,11 @@ export default class ExcalidrawView extends TextFileView {
|
||||
(await this.excalidrawData.syncElements(scene)) &&
|
||||
!this.autosaving
|
||||
) {
|
||||
//debug({where:"ExcalidrawView.save",file:this.file.name,dataTheme:this.excalidrawData.scene.appState.theme,before:"loadDrawing(false)"})
|
||||
await this.loadDrawing(false);
|
||||
}
|
||||
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/396
|
||||
const bakfilepath = getBakPath(this.file);
|
||||
/*const bakfilepath = getBakPath(this.file);
|
||||
try {
|
||||
if (await this.app.vault.adapter.exists(bakfilepath)) {
|
||||
await this.app.vault.adapter.remove(bakfilepath);
|
||||
@@ -300,15 +302,17 @@ export default class ExcalidrawView extends TextFileView {
|
||||
await this.app.vault.adapter.copy(this.file.path, bakfilepath);
|
||||
} catch(e) {
|
||||
console.error({where: "ExcalidrawView.save copy backup file", error: e});
|
||||
}*/
|
||||
|
||||
if(allowSave) {
|
||||
await super.save();
|
||||
this.diskIcon.querySelector("svg").removeClass("excalidraw-dirty");
|
||||
}
|
||||
|
||||
await super.save();
|
||||
|
||||
try {
|
||||
/*try {
|
||||
await this.app.vault.adapter.remove(bakfilepath);
|
||||
} catch(e) {
|
||||
console.error({where: "ExcalidrawView.save removing backup file", error: e});
|
||||
}
|
||||
}*/
|
||||
|
||||
if (!this.autosaving) {
|
||||
if (this.plugin.settings.autoexportSVG) {
|
||||
@@ -363,8 +367,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
header = header.replace(REG_IMG, "$1");
|
||||
}
|
||||
//end of remove
|
||||
|
||||
return header + this.excalidrawData.generateMD();
|
||||
if(!this.excalidrawData.disableCompression) {
|
||||
this.excalidrawData.disableCompression = this.isEditedAsMarkdownInOtherView();
|
||||
}
|
||||
const reuslt = header + this.excalidrawData.generateMD();
|
||||
this.excalidrawData.disableCompression = false;
|
||||
return reuslt;
|
||||
}
|
||||
if (this.compatibilityMode) {
|
||||
return JSON.stringify(scene, null, "\t");
|
||||
@@ -601,13 +609,14 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.zoomToFit(false);
|
||||
}
|
||||
|
||||
diskIcon: HTMLElement;
|
||||
onload() {
|
||||
this.addAction(SCRIPTENGINE_ICON_NAME, t("INSTALL_SCRIPT_BUTTON"), () => {
|
||||
new ScriptInstallPrompt(this.plugin).open();
|
||||
});
|
||||
|
||||
this.addAction(DISK_ICON_NAME, t("FORCE_SAVE"), async () => {
|
||||
await this.save(false);
|
||||
this.diskIcon = this.addAction(DISK_ICON_NAME, t("FORCE_SAVE"), async () => {
|
||||
await this.save(false, true);
|
||||
this.plugin.triggerEmbedUpdates();
|
||||
this.loadSceneFiles();
|
||||
});
|
||||
@@ -656,6 +665,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
);
|
||||
}
|
||||
this.setupAutosaveTimer();
|
||||
this.contentEl.addClass("excalidraw-view");
|
||||
}
|
||||
|
||||
public setTheme(theme: string) {
|
||||
@@ -684,7 +694,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.textIsParsed_Element.hide();
|
||||
}
|
||||
if (reload) {
|
||||
await this.save(false);
|
||||
await this.save(false,true);
|
||||
this.updateContainerSize();
|
||||
this.excalidrawAPI.history.clear(); //to avoid undo replacing links with parsed text
|
||||
}
|
||||
@@ -692,7 +702,9 @@ export default class ExcalidrawView extends TextFileView {
|
||||
|
||||
public setupAutosaveTimer() {
|
||||
const timer = async () => {
|
||||
if (this.dirty &&
|
||||
if (
|
||||
this.isLoaded &&
|
||||
this.dirty &&
|
||||
this.dirty == this.file?.path &&
|
||||
this.plugin.settings.autosave
|
||||
) {
|
||||
@@ -733,6 +745,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.preventReload = false;
|
||||
return;
|
||||
}
|
||||
this.diskIcon.querySelector("svg").removeClass("excalidraw-dirty");
|
||||
if (this.compatibilityMode) {
|
||||
this.dirty = null;
|
||||
return;
|
||||
@@ -743,8 +756,10 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if (!this.file) {
|
||||
return;
|
||||
}
|
||||
if (file) {
|
||||
const loadOnModifyTrigger = (file && file === this.file);
|
||||
if (loadOnModifyTrigger) {
|
||||
this.data = await this.app.vault.cachedRead(file);
|
||||
this.preventAutozoomOnLoad = true;
|
||||
}
|
||||
if (fullreload) {
|
||||
await this.excalidrawData.loadData(this.data, this.file, this.textMode);
|
||||
@@ -753,7 +768,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
this.excalidrawData.scene.appState.theme =
|
||||
this.excalidrawAPI.getAppState().theme;
|
||||
await this.loadDrawing(false);
|
||||
await this.loadDrawing(loadOnModifyTrigger);
|
||||
this.dirty = null;
|
||||
}
|
||||
|
||||
@@ -779,7 +794,6 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
data = this.data = data.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
this.dirty = null;
|
||||
this.compatibilityMode = this.file.extension === "excalidraw";
|
||||
await this.plugin.loadSettings();
|
||||
if (this.compatibilityMode) {
|
||||
@@ -789,7 +803,9 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if (!this.plugin.settings.compatibilityMode) {
|
||||
new Notice(t("COMPATIBILITY_MODE"), 4000);
|
||||
}
|
||||
this.excalidrawData.disableCompression = true;
|
||||
} else {
|
||||
this.excalidrawData.disableCompression = false;
|
||||
const textMode = getTextMode(data);
|
||||
this.changeTextMode(textMode, false);
|
||||
try {
|
||||
@@ -859,23 +875,17 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}, 500);
|
||||
}
|
||||
|
||||
initialContainerSizeUpdate = false;
|
||||
/**
|
||||
*
|
||||
* @param justloaded - a flag to trigger zoom to fit after the drawing has been loaded
|
||||
*/
|
||||
private async loadDrawing(justloaded: boolean) {
|
||||
const excalidrawData = this.excalidrawData.scene;
|
||||
const appState = excalidrawData.appState as AppState;
|
||||
if (appState.colorPalette?.canvasBackground && appState.colorPalette.canvasBackground.length !== 15) {
|
||||
new Notice ("appState.colorPalette.canvasBackground must have exactly 15 colors");
|
||||
}
|
||||
if (appState.colorPalette?.elementBackground && appState.colorPalette.elementBackground.length !== 15) {
|
||||
new Notice ("appState.colorPalette.elementBackground must have exactly 15 colors");
|
||||
}
|
||||
if (appState.colorPalette?.elementStroke && appState.colorPalette.elementStroke.length !== 15) {
|
||||
new Notice ("appState.colorPalette.elementStroke must have exactly 15 colors");
|
||||
}
|
||||
this.justLoaded = justloaded;
|
||||
this.initialContainerSizeUpdate =justloaded;
|
||||
this.dirty = null;
|
||||
this.diskIcon.querySelector("svg").removeClass("excalidraw-dirty");
|
||||
const om = this.excalidrawData.getOpenMode();
|
||||
this.preventReload = false;
|
||||
if (this.excalidrawRef) {
|
||||
@@ -927,6 +937,24 @@ export default class ExcalidrawView extends TextFileView {
|
||||
});
|
||||
//files are loaded on excalidrawRef readyPromise
|
||||
}
|
||||
const isCompressed = this.data.match(/```compressed\-json\n/gm) !== null;
|
||||
|
||||
if(
|
||||
!this.compatibilityMode &&
|
||||
(this.plugin.settings.compress !== isCompressed) &&
|
||||
!this.isEditedAsMarkdownInOtherView()
|
||||
) {
|
||||
this.dirty = this.file?.path;
|
||||
this.diskIcon.querySelector("svg").addClass("excalidraw-dirty");
|
||||
}
|
||||
}
|
||||
|
||||
isEditedAsMarkdownInOtherView():boolean {
|
||||
//if the user is editing the same file in markdown mode, do not compress it
|
||||
const leaves = this.app.workspace.getLeavesOfType("markdown");
|
||||
return leaves
|
||||
.filter(leaf => (leaf.view as MarkdownView).file === this.file)
|
||||
.length > 0;
|
||||
}
|
||||
|
||||
//Compatibility mode with .excalidraw files
|
||||
@@ -966,6 +994,10 @@ export default class ExcalidrawView extends TextFileView {
|
||||
.setTitle(t("OPEN_AS_MD"))
|
||||
.setIcon("document")
|
||||
.onClick(async () => {
|
||||
if(this.plugin.settings.compress === true) {
|
||||
this.excalidrawData.disableCompression = true;
|
||||
await this.save(true,true);
|
||||
}
|
||||
this.setMarkdownView();
|
||||
});
|
||||
})
|
||||
@@ -1102,11 +1134,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
return data?.library ? data.library : data?.libraryItems ?? [];
|
||||
}
|
||||
|
||||
previousSceneVersion = 0;
|
||||
private instantiateExcalidraw(initdata: any) {
|
||||
//console.log("ExcalidrawView.instantiateExcalidraw()");
|
||||
this.dirty = null;
|
||||
this.diskIcon.querySelector("svg").removeClass("excalidraw-dirty");
|
||||
const reactElement = React.createElement(() => {
|
||||
let previousSceneVersion = 0;
|
||||
let currentPosition = { x: 0, y: 0 };
|
||||
const excalidrawWrapperRef = React.useRef(null);
|
||||
const [dimensions, setDimensions] = React.useState({
|
||||
@@ -1293,20 +1326,18 @@ export default class ExcalidrawView extends TextFileView {
|
||||
return { id: imageElement[0].id, fileId: imageElement[0].fileId }; //return image element fileId
|
||||
};
|
||||
|
||||
this.addText = (text: string, fontFamily?: 1 | 2 | 3) => {
|
||||
this.addText = (text: string, fontFamily?: 1 | 2 | 3 | 4) => {
|
||||
if (!excalidrawRef?.current) {
|
||||
return;
|
||||
}
|
||||
const st: AppState = this.excalidrawAPI.getAppState();
|
||||
const ea = this.plugin.ea;
|
||||
ea.reset();
|
||||
ea.style.strokeColor = st.currentItemStrokeColor;
|
||||
ea.style.opacity = st.currentItemOpacity;
|
||||
ea.style.fontFamily = fontFamily
|
||||
? fontFamily
|
||||
: st.currentItemFontFamily;
|
||||
ea.style.fontSize = st.currentItemFontSize;
|
||||
ea.style.textAlign = st.currentItemTextAlign;
|
||||
ea.style.strokeColor = st.currentItemStrokeColor??"black";
|
||||
ea.style.opacity = st.currentItemOpacity??1;
|
||||
ea.style.fontFamily = fontFamily??st.currentItemFontFamily??1;
|
||||
ea.style.fontSize = st.currentItemFontSize??20;
|
||||
ea.style.textAlign = st.currentItemTextAlign??"left";
|
||||
ea.addText(currentPosition.x, currentPosition.y, text);
|
||||
this.addElements(ea.getElements(), false, true);
|
||||
};
|
||||
@@ -1410,6 +1441,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
await this.save(false); //preventReload=false will ensure that markdown links are paresed and displayed correctly
|
||||
} else {
|
||||
this.dirty = this.file?.path;
|
||||
this.diskIcon.querySelector("svg").addClass("excalidraw-dirty");
|
||||
}
|
||||
return true;
|
||||
};
|
||||
@@ -1807,21 +1839,26 @@ export default class ExcalidrawView extends TextFileView {
|
||||
viewModeEnabled = st.viewModeEnabled;
|
||||
if (this.justLoaded) {
|
||||
this.justLoaded = false;
|
||||
this.zoomToFit(false);
|
||||
previousSceneVersion = getSceneVersion(et);
|
||||
if(!this.preventAutozoomOnLoad) this.zoomToFit(false);
|
||||
this.preventAutozoomOnLoad = false;
|
||||
this.previousSceneVersion = getSceneVersion(et);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
st.editingElement == null &&
|
||||
st.resizingElement == null &&
|
||||
st.draggingElement == null &&
|
||||
st.editingGroupId == null &&
|
||||
st.editingLinearElement == null
|
||||
st.editingElement === null &&
|
||||
st.resizingElement === null &&
|
||||
st.draggingElement === null &&
|
||||
st.editingGroupId === null &&
|
||||
st.editingLinearElement === null
|
||||
) {
|
||||
const sceneVersion = getSceneVersion(et);
|
||||
if (sceneVersion != previousSceneVersion) {
|
||||
previousSceneVersion = sceneVersion;
|
||||
if (
|
||||
sceneVersion > 0 &&
|
||||
sceneVersion !== this.previousSceneVersion
|
||||
) {
|
||||
this.previousSceneVersion = sceneVersion;
|
||||
this.dirty = this.file?.path;
|
||||
this.diskIcon.querySelector("svg").addClass("excalidraw-dirty");
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1912,7 +1949,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
currentPosition.y,
|
||||
draggable.file,
|
||||
);
|
||||
ea.addElementsToView(false, false);
|
||||
ea.addElementsToView(false, false, true);
|
||||
})();
|
||||
return false;
|
||||
}
|
||||
@@ -2018,6 +2055,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
originalText: string,
|
||||
isDeleted: boolean,
|
||||
): [string, string, string] => {
|
||||
this.isEditingText = true;
|
||||
this.isEditingTextResetTimer = setTimeout(() => {
|
||||
this.isEditingText = false;
|
||||
this.isEditingTextResetTimer = null;
|
||||
@@ -2026,6 +2064,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if (isDeleted) {
|
||||
this.excalidrawData.deleteTextElement(textElement.id);
|
||||
this.dirty = this.file?.path;
|
||||
this.diskIcon.querySelector("svg").addClass("excalidraw-dirty");
|
||||
this.setupAutosaveTimer();
|
||||
return [null, null, null];
|
||||
}
|
||||
@@ -2187,7 +2226,13 @@ export default class ExcalidrawView extends TextFileView {
|
||||
.filter((el: ExcalidrawElement) =>
|
||||
el.boundElements?.map((e) => e.type).includes("text"),
|
||||
);
|
||||
api.updateContainerSize(containers);
|
||||
if(containers.length > 0) {
|
||||
if(this.initialContainerSizeUpdate) { //updateContainerSize will bump scene version which will trigger a false autosave
|
||||
this.justLoaded = true; //after load, which will lead to a ping-pong between two syncronizing devices
|
||||
}
|
||||
api.updateContainerSize(containers);
|
||||
}
|
||||
this.initialContainerSizeUpdate = false;
|
||||
};
|
||||
if (delay) {
|
||||
setTimeout(() => update(), 50);
|
||||
@@ -2197,7 +2242,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
|
||||
public zoomToFit(delay: boolean = true) {
|
||||
if (!this.excalidrawRef) {
|
||||
if (!this.excalidrawRef || this.isEditingText) {
|
||||
return;
|
||||
}
|
||||
const maxZoom = this.plugin.settings.zoomToFitMaxLevel;
|
||||
|
||||
@@ -45,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);
|
||||
})();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
})();
|
||||
}
|
||||
|
||||
|
||||
@@ -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[] = [
|
||||
|
||||
13
src/Utils.ts
13
src/Utils.ts
@@ -21,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 {
|
||||
@@ -125,10 +126,10 @@ export function getIMGPathFromExcalidrawFile(
|
||||
);
|
||||
}
|
||||
|
||||
export function getBakPath(file:TFile):string {
|
||||
/*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
|
||||
@@ -550,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 });
|
||||
};
|
||||
|
||||
142
src/constants.ts
142
src/constants.ts
@@ -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"/>`;
|
||||
|
||||
@@ -45,6 +45,7 @@ export default {
|
||||
"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",
|
||||
@@ -64,7 +65,7 @@ export default {
|
||||
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)",
|
||||
@@ -96,9 +97,19 @@ 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. ",
|
||||
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, or 1, 2, 3, 4, or 5 minutes. Save normally happens when you close Excalidraw or Obsidian, or move " +
|
||||
"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",
|
||||
@@ -246,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",
|
||||
|
||||
132
src/main.ts
132
src/main.ts
@@ -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"),
|
||||
@@ -942,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;
|
||||
}
|
||||
@@ -1253,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;
|
||||
}
|
||||
@@ -1295,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) => {
|
||||
@@ -1372,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);
|
||||
@@ -1412,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}]]` : `})`);
|
||||
editor.focus();
|
||||
return;
|
||||
}
|
||||
@@ -1424,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]]%%`
|
||||
: `})\n%%[🖋 Edit in Excalidraw](${encodeURI(data)})%%`,
|
||||
);
|
||||
editor.focus();
|
||||
}
|
||||
@@ -1514,18 +1594,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
) {
|
||||
let data = await this.app.vault.read(template);
|
||||
if (data) {
|
||||
if(this.settings.matchTheme) {
|
||||
if(isObsidianThemeDark) {
|
||||
if((data.match(/"theme"\s*:\s*"light"\s*,/g)||[]).length === 1) {
|
||||
data = data.replace(/"theme"\s*:\s*"light"\s*,/,`"theme": "dark",`);
|
||||
}
|
||||
} else {
|
||||
if((data.match(/"theme"\s*:\s*"dark"\s*,/g)||[]).length === 1) {
|
||||
data = data.replace(/"theme"\s*:\s*"dark"\s*,/,`"theme": "light",`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return data;
|
||||
return this.settings.matchTheme ? changeThemeOfExcalidrawMD(data) : data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1538,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)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1569,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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1633,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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface ExcalidrawSettings {
|
||||
embedUseExcalidrawFolder: boolean;
|
||||
templateFilePath: string;
|
||||
scriptFolderPath: string;
|
||||
compress: boolean;
|
||||
autosave: boolean;
|
||||
autosaveInterval: number;
|
||||
drawingFilenamePrefix: string;
|
||||
@@ -49,6 +50,7 @@ export interface ExcalidrawSettings {
|
||||
autoexportPNG: boolean;
|
||||
autoexportExcalidraw: boolean;
|
||||
embedType: "excalidraw" | "PNG" | "SVG";
|
||||
embedWikiLink: boolean,
|
||||
syncExcalidraw: boolean;
|
||||
compatibilityMode: boolean;
|
||||
experimentalFileType: boolean;
|
||||
@@ -79,8 +81,9 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
embedUseExcalidrawFolder: false,
|
||||
templateFilePath: "Excalidraw/Template.excalidraw",
|
||||
scriptFolderPath: "Excalidraw/Scripts",
|
||||
compress: false,
|
||||
autosave: true,
|
||||
autosaveInterval: 30000,
|
||||
autosaveInterval: 15000,
|
||||
drawingFilenamePrefix: "Drawing ",
|
||||
drawingEmbedPrefixWithFilename: true,
|
||||
drawingFilenameDateTime: "YYYY-MM-DD HH.mm.ss",
|
||||
@@ -112,6 +115,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
autoexportPNG: false,
|
||||
autoexportExcalidraw: false,
|
||||
embedType: "excalidraw",
|
||||
embedWikiLink: true,
|
||||
syncExcalidraw: false,
|
||||
experimentalFileType: false,
|
||||
experimentalFileTag: "✏️",
|
||||
@@ -261,6 +265,18 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
}),
|
||||
);
|
||||
|
||||
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");
|
||||
@@ -345,6 +361,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
.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");
|
||||
@@ -774,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)
|
||||
|
||||
10
styles.css
10
styles.css
@@ -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*/
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"1.6.11": "0.12.16",
|
||||
"1.6.14": "0.12.16",
|
||||
"1.4.2": "0.11.13"
|
||||
}
|
||||
|
||||
18
yarn.lock
18
yarn.lock
@@ -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-49":
|
||||
"integrity" "sha512-32XoMHNyvzWOkkwBMzlS7L6ijabWxCmmA24Pvpn0PUkVQf9dy8OhDDtMHd7NmxD8zglnAXihlAMhTX/l+CLo3A=="
|
||||
"resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.10.0-obsidian-49.tgz"
|
||||
"version" "0.10.0-obsidian-49"
|
||||
"@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"
|
||||
|
||||
Reference in New Issue
Block a user