This commit is contained in:
Zsolt Viczian
2022-01-25 22:08:42 +01:00
parent a285e1aeee
commit 4fd5c13d1e
10 changed files with 234 additions and 214 deletions

View File

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

View File

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

View File

@@ -786,10 +786,11 @@ export async function initExcalidrawAutomate(
: 0.1,
gap: GAP,
},
startArrowhead: formatting?.startArrowHead
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/388
startArrowhead: typeof (formatting?.startArrowHead) !== "undefined"
? formatting.startArrowHead
: this.style.startArrowHead,
endArrowhead: formatting?.endArrowHead
endArrowhead: typeof (formatting?.endArrowHead) !== "undefined"
? formatting.endArrowHead
: this.style.endArrowHead,
...boxedElement(id, "arrow", points[0][0], points[0][1], box.w, box.h),

View File

@@ -81,7 +81,8 @@ export const REGEX_LINK = {
export const REG_LINKINDEX_HYPERLINK = /^\w+:\/\//;
const DRAWING_REG = /\n# Drawing\n[^`]*(```json\n)([\s\S]*?)```/gm; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/182
//added \n at and of DRAWING_REG: https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/357
const DRAWING_REG = /\n# Drawing\n[^`]*(```json\n)([\s\S]*?)```\n/gm; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/182
const DRAWING_REG_FALLBACK = /\n# Drawing\n(```json\n)?(.*)(```)?(%%)?/gm;
export function getJSON(data: string): { scene: string; pos: number } {
let res = data.matchAll(DRAWING_REG);
@@ -105,11 +106,7 @@ export function getJSON(data: string): { scene: string; pos: number } {
}
export function getMarkdownDrawingSection(jsonString: string) {
return `%%\n# Drawing\n${String.fromCharCode(96)}${String.fromCharCode(
96,
)}${String.fromCharCode(96)}json\n${jsonString}\n${String.fromCharCode(
96,
)}${String.fromCharCode(96)}${String.fromCharCode(96)}\n%%`;
return `%%\n# Drawing\n\x60\x60\x60json\n${jsonString}\n\x60\x60\x60\n%%`;
}
/**
@@ -382,11 +379,7 @@ export class ExcalidrawData {
newOriginalText: string,
forceUpdate: boolean = false,
) {
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/376
if (sceneTextElement.containerId) {
return; //I leave the setting of text size to excalidraw, when text is in a container
//because text width is fixed to the container width
}
if (forceUpdate || newText != sceneTextElement.text) {
const measure = measureText(
newText,
@@ -395,7 +388,13 @@ export class ExcalidrawData {
);
sceneTextElement.text = newText;
sceneTextElement.originalText = newOriginalText;
sceneTextElement.width = measure.w;
if (!sceneTextElement.containerId) {
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/376
//I leave the setting of text width to excalidraw, when text is in a container
//because text width is fixed to the container width
sceneTextElement.width = measure.w;
}
sceneTextElement.height = measure.h;
sceneTextElement.baseline = measure.baseline;
}

View File

@@ -1942,7 +1942,7 @@ export default class ExcalidrawView extends TextFileView {
this.isEditingTextResetTimer = setTimeout(() => {
this.isEditingText = false;
this.isEditingTextResetTimer = null;
}, 300); // to give time for the onscreen keyboard to disappear
}, 1500); // to give time for the onscreen keyboard to disappear
if (isDeleted) {
this.excalidrawData.deleteTextElement(textElement.id);

View File

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

View File

@@ -1,9 +1,10 @@
import { App, Instruction, TAbstractFile, TFile } from "obsidian";
import { App, Instruction, Notice, TAbstractFile, TFile } from "obsidian";
import { PLUGIN_ID, VIEW_TYPE_EXCALIDRAW } from "./constants";
import ExcalidrawView from "./ExcalidrawView";
import { t } from "./lang/helpers";
import ExcalidrawPlugin from "./main";
import { GenericInputPrompt, GenericSuggester } from "./Prompt";
import { splitFolderAndFilename } from "./Utils";
import { errorlog, splitFolderAndFilename } from "./Utils";
export class ScriptEngine {
private plugin: ExcalidrawPlugin;
@@ -70,20 +71,23 @@ export class ScriptEngine {
this.loadScripts();
}
loadScripts() {
public getListofScripts(): TFile[] {
const app = this.plugin.app;
this.scriptPath = this.plugin.settings.scriptFolderPath;
if (!app.vault.getAbstractFileByPath(this.scriptPath)) {
this.scriptPath = null;
return;
}
const scripts = app.vault
return app.vault
.getFiles()
.filter((f: TFile) => f.path.startsWith(this.scriptPath));
scripts.forEach((f) => this.loadScript(f));
}
getScriptName(f: TFile | string): string {
loadScripts() {
this.getListofScripts()?.forEach((f) => this.loadScript(f));
}
public getScriptName(f: TFile | string): string {
let basename = "";
let path = "";
if (f instanceof TFile) {
@@ -161,9 +165,9 @@ export class ScriptEngine {
//https://stackoverflow.com/questions/45381204/get-asyncfunction-constructor-in-typescript changed tsconfig to es2017
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
const result = await new AsyncFunction("ea", "utils", script)(
this.plugin.ea,
{
let result = null;
try {
result = await new AsyncFunction("ea", "utils", script)(this.plugin.ea, {
inputPrompt: (header: string, placeholder?: string, value?: string) =>
ScriptEngine.inputPrompt(this.plugin.app, header, placeholder, value),
suggester: (
@@ -179,8 +183,11 @@ export class ScriptEngine {
hint,
instructions,
),
},
);
});
} catch (e) {
new Notice(t("SCRIPT_EXECUTION_ERROR"), 4000);
errorlog({ script: this.plugin.ea.activeScript, error: e });
}
this.plugin.ea.activeScript = null;
return result;
}

View File

@@ -316,4 +316,8 @@ export default {
//EmbeddedFileLoader.ts
INFINITE_LOOP_WARNING:
"EXCALIDRAW WARNING\nAborted loading embedded images due to infinite loop in file:\n",
//Scripts.ts
SCRIPT_EXECUTION_ERROR:
"Script execution error. Please find error message on the developer console.",
};

View File

@@ -1,3 +1,4 @@
import { borderTopRightRadius } from "html2canvas/dist/types/css/property-descriptors/border-radius";
import {
App,
DropdownComponent,
@@ -913,7 +914,13 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
);
});
if (Object.keys(this.plugin.settings.scriptEngineSettings).length > 0) {
const scripts = this.plugin.scriptEngine
.getListofScripts()
?.map((f) => this.plugin.scriptEngine.getScriptName(f));
if (
Object.keys(this.plugin.settings.scriptEngineSettings).length > 0 &&
scripts
) {
const getValue = (scriptName: string, variableName: string): any => {
const variable =
//@ts-ignore
@@ -1034,8 +1041,9 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
};
this.containerEl.createEl("h1", { text: t("SCRIPT_SETTINGS_HEAD") });
Object.keys(this.plugin.settings.scriptEngineSettings).forEach(
(scriptName: string) => {
Object.keys(this.plugin.settings.scriptEngineSettings)
.filter((s) => scripts.contains(s))
.forEach((scriptName: string) => {
const settings =
//@ts-ignore
this.plugin.settings.scriptEngineSettings[scriptName];
@@ -1084,8 +1092,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
break;
}
});
},
);
});
}
}
}

View File

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