Compare commits

..

49 Commits

Author SHA1 Message Date
Zsolt Viczian
7055f08c35 1.5.21 2022-01-23 15:22:30 +01:00
Zsolt Viczian
637174fe3d add image 2022-01-23 15:07:14 +01:00
Zsolt Viczian
cabc05d2ce add image 2022-01-23 14:39:44 +01:00
Zsolt Viczian
871d924289 uploading images 2022-01-23 11:54:34 +01:00
Zsolt Viczian
b9d8c0fe44 Added new script 2022-01-22 20:13:57 +01:00
Zsolt Viczian
656e551ac3 updated scripts 2022-01-22 19:53:27 +01:00
Zsolt Viczian
053f3fbc08 updated scripts 2022-01-22 19:48:17 +01:00
Zsolt Viczian
10c16b8df1 1.5.20 2022-01-22 18:48:04 +01:00
Zsolt Viczian
1fb233c27f bump packages 2022-01-22 17:43:24 +01:00
zsviczian
43175d60f9 Merge pull request #381 from 1-2-3/master
fix: not ignore individual arrows in script `Fixed vertical distance`
2022-01-22 16:59:46 +01:00
zahuifan
2d18aa1d73 fix: not ignore individual arrows 2022-01-22 23:23:03 +08:00
Zsolt Viczian
20017f8e8b filename settings, embedded image text alignment 2022-01-22 10:45:29 +01:00
Zsolt Viczian
504db44273 update text element to sticky note 2022-01-21 19:02:48 +01:00
Zsolt Viczian
ffa230e375 convert selected text elements to sticky notes 2022-01-21 18:57:11 +01:00
zsviczian
ac7266eefd Merge pull request #379 from tswwe/master
Update zh-cn.ts to 1.5.17
2022-01-21 17:55:57 +01:00
zsviczian
bda7db0777 Merge pull request #378 from 1-2-3/master
Ignore individual arrows in Fixed spacing and Fixed vertical distance scripts
2022-01-21 17:52:37 +01:00
thxnder
7605a105ee Update zh-cn.ts to 1.5.17
Some adjustment, correction and complement to keep it updated with the newest 1.5.17 en.ts.
2022-01-22 00:07:19 +08:00
zahuifan
9705799adf ignore individual arrows in Fixed spacing etc. 2022-01-21 20:27:26 +08:00
zsviczian
340c96cfe4 Merge pull request #375 from 1-2-3/master
Add Normalize Selected Arrows ea-script file
2022-01-21 09:45:52 +01:00
zahuifan
114cad2256 Add tips description to script 2022-01-20 15:25:07 +08:00
zahuifan
b64ca8e43d Add Normalize Selected Arrows ea-script file 2022-01-20 14:45:28 +08:00
zsviczian
94d234cffc Merge pull request #371 from 1-2-3/master
fix #370 Add "line" to the Darken & Lighten background color scripts
2022-01-18 07:32:05 +01:00
zahuifan
e1135d13fd fix #370 Add "line" to the Darken & Lighten script 2022-01-18 10:26:58 +08:00
zsviczian
2bff6d87da Merge pull request #369 from 1-2-3/master
handle arrows when adding boxes or changing sizes
2022-01-17 11:35:29 +01:00
zahuifan
1e8ef02944 handle arrows when adding boxes or changing sizes 2022-01-17 10:18:51 +08:00
Zsolt Viczian
d8a5e26030 1.5.19 2022-01-16 21:00:50 +01:00
Zsolt Viczian
d6c686d230 1.5.18 2022-01-16 20:35:46 +01:00
Zsolt Viczian
d7446d20dd 1.5.17 2022-01-16 13:19:04 +01:00
zsviczian
541db2ca2f Merge pull request #368 from 1-2-3/master
Ignore individual lines when calculating maximum width and height
2022-01-16 06:20:58 +01:00
zahuifan
b0fc21b70a Ignore individual lines when calculating max width 2022-01-16 11:16:49 +08:00
zsviczian
0b36759f09 Update README.md 2022-01-15 12:09:15 +01:00
zsviczian
c94ebb6bcd Merge pull request #362 from 1-2-3/master
Add two new ea-scripts
2022-01-15 11:56:04 +01:00
zahuifan
75e179041d Add two new ea-scripts 2022-01-15 15:34:48 +08:00
zsviczian
3d41690359 Create codeql-analysis.yml 2022-01-14 21:52:35 +01:00
zsviczian
e3d31f49de Merge pull request #358 from 1-2-3/master
Fix unnecessary changes to text placement
2022-01-14 19:07:13 +01:00
zahuifan
ee48840421 Fix unnecessary changes to text placement 2022-01-14 13:40:48 +08:00
zsviczian
b8b08b3edb Merge pull request #356 from 1-2-3/master
Fixed rearranging text incorrectly when a rectangle contains multiple text elements.
2022-01-13 11:16:34 +01:00
zahuifan
d1f994a8d1 Fixed incorrect text placement 2022-01-13 17:50:44 +08:00
Zsolt Viczian
2a8aafeab0 1.5.16 2022-01-10 22:13:27 +01:00
zsviczian
d1ab96f9d1 Merge pull request #346 from 1-2-3/master
Fixed an issue where shortcut keys such as Ctrl+Shift+Left failed after script execution
2022-01-08 12:03:52 +01:00
Zsolt Viczian
1bdf0a8089 fixed focus #346 2022-01-08 12:03:39 +01:00
zahuifan
d8f4d55b76 Fixed shortcut keys failed after script execution 2022-01-08 11:52:26 +08:00
Zsolt Viczian
2d16b59ea3 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2022-01-07 19:34:46 +01:00
Zsolt Viczian
b292ca0fa3 1.5.15 2022-01-07 19:34:24 +01:00
zsviczian
95c21c2d5e Update README.md 2022-01-06 15:25:27 +01:00
zsviczian
7e45a0f952 Update index.md 2022-01-06 07:50:20 +01:00
zsviczian
7c8460646a Merge pull request #343 from 1-2-3/master
Add modify background color opacity scripts image
2022-01-06 07:47:44 +01:00
zahuifan
8badc3eb8f Add modify background color opacity scripts image 2022-01-06 10:10:19 +08:00
Zsolt Viczian
ad98d114e1 scriptSettings 2022-01-05 21:09:36 +01:00
61 changed files with 8887 additions and 8785 deletions

70
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '38 14 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@@ -13,6 +13,7 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
|[![9 Excalidraw Automate](https://user-images.githubusercontent.com/14358394/125160367-bdb6cc00-e17c-11eb-92f1-6f59faea85fd.jpg)](https://youtu.be/VRZVujfVab0)|[![10 Miscellaneous](https://user-images.githubusercontent.com/14358394/125160374-c3141680-e17c-11eb-8cc2-dfaffd903d15.jpg)](https://youtu.be/D1iBYo1_jjc)|[![Image Elements](https://user-images.githubusercontent.com/14358394/138607067-ccb62f92-48a4-4880-ac6e-68c1bf86ac2c.png)](https://www.youtube.com/watch?v=_c_0zpBJ4Xc&)|
|[![LaTex Demo](https://user-images.githubusercontent.com/14358394/143732412-1c65227e-4381-406d-847a-b001ab3506ca.jpg)](https://youtu.be/r08wk-58DPk)|[![markdown embeds](https://user-images.githubusercontent.com/14358394/143732440-90bfa029-8615-462e-ada3-c903d71a82c9.jpg)](https://youtu.be/tsecSfnTMow)|[![markdownAdvanced](https://user-images.githubusercontent.com/14358394/143783906-15cee494-c6d5-4495-a2ca-74634e4e7355.jpg)](https://youtu.be/K6qZkTz8GHs)|
|[![Script Engine](https://user-images.githubusercontent.com/14358394/145684531-8d9c2992-59ac-4ebc-804a-4cce1777ded2.jpg)](https://youtu.be/hePJcObHIso)|[![sticky notes thumbnail](https://user-images.githubusercontent.com/14358394/147283367-e5689385-ea51-4983-81a3-04d810d39f62.jpg)](https://youtu.be/NOuddK6xrr8)|[![plugin store](https://user-images.githubusercontent.com/14358394/147889174-6c306d0d-2d29-46cc-a53f-3f0013cf14de.jpg)](https://youtu.be/lzYdOQ6z8F0)|
|[![fourtfont](https://user-images.githubusercontent.com/14358394/149659524-2a4e0a24-40c9-4e66-a6b1-c92f3b88ecd5.jpg)](https://youtu.be/eKFmrSQhFA4)|||
# Key features
@@ -26,7 +27,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. Check below and 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
@@ -61,6 +62,7 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
- `excalidraw-link-prefix: "📍"` preview prefix for internal links
- `excalidraw-url-prefix: "🌐"` preview prefix for external links
- `excalidraw-link-brackets: true|false` whether or not to display brackets around links in preview
- `excalidraw-default-mode: view|zen` Open this document in view mode or zen mode by defult. Default view mode is excellent for presentation slides.
- Embed complete markdown files into your drawings
- Drag from the desired file from the Obsidian file explorer and hold down CTRL/CMD while dropping the file onto the canvas.
- Use the command palette action: `Insert markdown file from vault`

View File

@@ -17,7 +17,7 @@ export interface ExcalidrawAutomate {
roughness: number;
opacity: number;
strokeSharpness: StrokeSharpness; //type StrokeSharpness = "round" | "sharp"
fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia
fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont
fontSize: number;
textAlign: string; //"left"|"right"|"center"
verticalAlign: string; //"top"|"bottom"|"middle" :for future use, has no effect currently
@@ -134,8 +134,13 @@ export interface ExcalidrawAutomate {
},
): boolean;
addElementsToView( //Adds elements from elementsDict to the current view
repositionToCursor: boolean,
save: boolean,
repositionToCursor?: boolean, //default is false
save?: boolean, //default is true
//newElementsOnTop controls whether elements created with ExcalidrawAutomate
//are added at the bottom of the stack or the top of the stack of elements already in the view
//Note that elements copied to the view with copyViewElementsToEAforEditing retain their
//position in the stack of elements in the view even if modified using EA
newElementsOnTop?: boolean, //default is false, i.e. the new elements get to the bottom of the stack
): Promise<boolean>;
onDropHook(data: {
//if set Excalidraw will call this function onDrop events
@@ -176,6 +181,17 @@ export interface ExcalidrawAutomate {
b: readonly [number, number],
gap?: number, //if given, element is inflated by this value
): Point[];
//See OCR plugin for example on how to use scriptSettings
activeScript: string; //Set automatically by the ScriptEngine
getScriptSettings(): {}; //Returns script settings. Saves settings in plugin settings, under the activeScript key
setScriptSettings(settings:any):Promise<void>; //sets script settings.
openFileInNewOrAdjacentLeaf (file:TFile):WorkspaceLeaf;//Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings
measureText(text:string):{ width: number, height: number }; //measure text size based on current style settings
//verifyMinimumPluginVersion returns true if plugin version is >= than required
//recommended use:
//if(!(ea.verifyMinimumPluginVersion && ea.verifyMinimumPluginVersion("1.5.20"))) {new Notice("message");return;}
verifyMinimumPluginVersion(requiredVersion: string):boolean;
}
```

View File

@@ -26,9 +26,52 @@ An Excalidraw script will automatically receive two objects:
- `inputPrompt: (header: string, placeholder?: string, value?: string)`
- Opens a prompt that asks for an input. Returns a string with the input.
- You need to await the result of inputPrompt.
- `suggester: (displayItems: string[], actualItems: string[])`
- Opens a suggester. Displays the displayItems, but you map these the other values with actualItems. Returns the selected value.
- `suggester: (displayItems: string[], items: any[], hint?: string, instructions?:Instruction[])`
- Opens a suggester. Displays the displayItems and returns the corresponding item from items[].
- You need to await the result of suggester.
- If the user cancels (ESC), suggester will return `undefined`
- Hint and instructions are optional.
```typescript
interface Instruction {
command: string;
purpose: string;
}
```
- Scripts may have settings. These settings are stored as part of plugin settings and may be also changed by the user via the Obsidian plugin settings window.
- You can access settings for the active script using `ea.getScriptSettings()` and store settings values with `ea.setScriptSettings(settings:any)`
- Rules for displaying script settings in plugin settings are:
- If the setting is a simple literal (boolean, number, string) these will be displayed as such in settings. The name of the setting will be the key for the value.
```javascript
ea.setScriptSettings({
"value 1": true,
"value 2": 1,
"value 3": "my string"
})
```
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/SimpleSettings.jpg)
- If the setting is an object and follows the below structure then a description and a valueset may also be added. Values may also be hidden from the user using the `hidden` key.
```javascript
ea.setScriptSettings({
"value 1": {
"value": true,
"description": "This is the description for my boolean value"
},
"value 2": {
"value": 1,
"description": "This is the description for my numeric value"
},
"value 3": {
"value": "my string",
"description": "This is the description for my string value",
"valueset": ["allowed 1","allowed 2","allowed 3"]
},
"value 4": {
"value": "my value",
"hidden": true
}
});
```
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/ComplexSettings.jpg)
---------
@@ -42,16 +85,41 @@ These scripts are available as downloadable `.md` files on GitHub in [this](http
This script will add an encapsulating box around the currently selected elements in Excalidraw
```javascript
//uncomment if you want a prompt for custom padding
//const padding = parseInt (await utils.inputPrompt("padding?","number","10"));
const padding = 10
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
settings = ea.getScriptSettings();
//check if settings exist. If not, set default values on first run
if(!settings["Default padding"]) {
settings = {
"Prompt for padding?": true,
"Default padding" : {
value: 10,
description: "Padding between the bounding box of the selected elements, and the box the script creates"
}
};
ea.setScriptSettings(settings);
}
let padding = settings["Default padding"].value;
if(settings["Prompt for padding?"]) {
padding = parseInt (await utils.inputPrompt("padding?","number",padding.toString()));
}
if(isNaN(padding)) {
new Notice("The padding value provided is not a number");
return;
}
elements = ea.getViewSelectedElements();
const box = ea.getBoundingBox(elements);
color = ea
.getExcalidrawAPI()
.getAppState()
.currentItemStrokeColor;
//uncomment if you want to set the stroke to a random color
//uncomment for random color:
//color = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,"0");
ea.style.strokeColor = color;
id = ea.addRect(
@@ -73,20 +141,76 @@ ea.addElementsToView(false);
This script will connect two objects with an arrow. If either of the objects are a set of grouped elements (e.g. a text element grouped with an encapsulating rectangle), the script will identify these groups, and connect the arrow to the largest object in the group (assuming you want to connect the arrow to the box around the text element).
```javascript
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
settings = ea.getScriptSettings();
//set default values on first run
if(!settings["Starting arrowhead"]) {
settings = {
"Starting arrowhead" : {
value: "none",
valueset: ["none","arrow","triangle","bar","dot"]
},
"Ending arrowhead" : {
value: "triangle",
valueset: ["none","arrow","triangle","bar","dot"]
},
"Line points" : {
value: 1,
description: "Number of line points between start and end"
}
};
ea.setScriptSettings(settings);
}
const arrowStart = settings["Starting arrowhead"].value === "none" ? null : settings["Starting arrowhead"].value;
const arrowEnd = settings["Ending arrowhead"].value === "none" ? null : settings["Ending arrowhead"].value;
const linePoints = Math.floor(settings["Line points"].value);
const elements = ea.getViewSelectedElements();
ea.copyViewElementsToEAforEditing(elements);
const groups = ea.getMaximumGroups(elements);
if(groups.length !== 2) return;
groups = ea.getMaximumGroups(elements);
if(groups.length !== 2) {
//unfortunately getMaxGroups returns duplicated resultset for sticky notes
//needs additional filtering
cleanGroups=[];
idList = [];
for (group of groups) {
keep = true;
for(item of group) if(idList.contains(item.id)) keep = false;
if(keep) {
cleanGroups.push(group);
idList = idList.concat(group.map(el=>el.id))
}
}
if(cleanGroups.length !== 2) return;
groups = cleanGroups;
}
els = [
ea.getLargestElement(groups[0]),
ea.getLargestElement(groups[1])
];
ea.style.strokeColor = els[0].strokeColor;
ea.style.strokeWidth = els[0].strokeWidth;
ea.style.strokeStyle = els[0].strokeStyle;
ea.style.strokeSharpness = els[0].strokeSharpness;
ea.connectObjects(
els[0].id,
null,
els[1].id,
null,
{numberOfPoints:2}
{
endArrowHead: arrowEnd,
startArrowHead: arrowStart,
numberOfPoints: linePoints
}
);
ea.addElementsToView();
```

View File

@@ -1,9 +1,4 @@
/*
![](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-bullet-point.jpg)
This script will add a small circle to the top left of each text element in the selection and add the text and the "bullet point" into a group.

View File

@@ -0,0 +1,72 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-link-and-open.jpg)
Prompt for a file in the vault. Add a link above or below (based on settings) the selected element, to the selected file. If no file is selected then the script creates a new file following the default filename defined for excalidraw embeds.
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
settings = ea.getScriptSettings();
//set default values on first run
if(!settings["Link position"]) {
settings = {
"Link position" : {
value: "below",
valueset: ["above","below"],
description: "Add link below or above the selected object?"
},
"Link font size" : {
value: 12
}
};
ea.setScriptSettings(settings);
}
const below = settings["Link position"].value === "below";
const fontSize = Math.floor(settings["Link font size"].value);
elements = ea.getViewSelectedElements();
if(elements.length === 0) {
new Notice("No selected elements");
return;
}
const files = app.vault.getFiles()
const filePaths = files.map((f)=>f.path);
file = await utils.suggester(filePaths,files,"Select file or press ESC to create a new document");
alias = null;
if(file) {
alias = file.basename;
} else {
const prefix = ea.targetView.file.path.substring(0,ea.targetView.file.path.length-3);
const timestamp = moment(Date.now()).format(ea.plugin.settings.drawingFilenameDateTime);
file = await app.vault.create(`${prefix} ${timestamp}.md`,"");
}
const filepath = app.metadataCache.fileToLinktext(file,ea.targetView.file.path,true);
ea.style.textAlign = "center";
ea.style.fontSize = fontSize;
const textElementsIfAny = elements.filter(el=>el.type==="text");
if(textElementsIfAny.length>0) ea.style.fontFamily = textElementsIfAny[0].fontFamily;
ea.style.strokeColor = elements[0].strokeColor;
const box = ea.getBoundingBox(elements);
const linkText = `[[${filepath}${alias?"|"+alias:""}]]`;
const size = ea.measureText(alias?ea.plugin.settings.linkPrefix+alias:linkText);
const id = ea.addText(
box.topX+(box.width-size.width)/2,
below ? box.topY + box.height + size.height : box.topY - size.height - 3,
linkText
);
ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup(elements.map((e)=>e.id).concat([id]));
ea.addElementsToView(false);
ea.openFileInNewOrAdjacentLeaf(file);

View File

@@ -0,0 +1,114 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-process-step.jpg)
This script will prompt you for the title of the process step, then will create a stick note with the text. If an element is selected then the script will connect this new step with an arrow to the previous step (the selected element). If no element is selected, then the script assumes this is the first step in the process and will only output the sticky note with the text that was entered.
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
settings = ea.getScriptSettings();
//set default values on first run
if(!settings["Starting arrowhead"]) {
settings = {
"Starting arrowhead" : {
value: "none",
valueset: ["none","arrow","triangle","bar","dot"]
},
"Ending arrowhead" : {
value: "triangle",
valueset: ["none","arrow","triangle","bar","dot"]
},
"Line points" : {
value: 0,
description: "Number of line points between start and end"
},
"Gap between elements": {
value: 100
},
"Wrap text at (number of characters)": {
value: 25,
},
"Fix width": {
value: true,
description: "The object around the text should have fix width to fit the wrapped text"
}
};
ea.setScriptSettings(settings);
}
const arrowStart = settings["Starting arrowhead"].value === "none" ? null : settings["Starting arrowhead"].value;
const arrowEnd = settings["Ending arrowhead"].value === "none" ? null : settings["Ending arrowhead"].value;
const linePoints = Math.floor(settings["Line points"].value);
const gapBetweenElements = Math.floor(settings["Gap between elements"].value);
const wrapLineLen = Math.floor(settings["Wrap text at (number of characters)"].value);
const fixWidth = settings["Fix width"];
const textPadding = 10;
const text = await utils.inputPrompt("Text?");
const elements = ea.getViewSelectedElements();
const isFirst = (!elements || elements.length === 0);
const width = ea.measureText("w".repeat(wrapLineLen)).width;
console.log(width,fixWidth);
if(!isFirst) {
const fromElement = ea.getLargestElement(elements);
ea.copyViewElementsToEAforEditing([fromElement]);
const previousTextElements = elements.filter((el)=>el.type==="text");
if(previousTextElements.length>0) {
const el = previousTextElements[0];
ea.style.strokeColor = el.strokeColor;
ea.style.fontSize = el.fontSize;
ea.style.fontFamily = el.fontFamily;
ea.style.strokeWidth = el.strokeWidth;
ea.style.strokeStyle = el.strokeStyle;
ea.style.strokeSharpness = el.strokeSharpness;
}
const id = ea.addText(
fromElement.x,
fromElement.y+fromElement.height+gapBetweenElements,
text,
{
wrapAt: wrapLineLen,
textAlign: "center",
box: "rectangle",
boxPadding: textPadding,
...fixWidth?{width: width}:null
}
);
ea.connectObjects(
fromElement.id,
null,
id,
null,
{
endArrowHead: arrowEnd,
startArrowHead: arrowStart,
numberOfPoints: linePoints
}
);
ea.addElementsToView(false);
} else {
ea.addText(
0,
0,
text,
{
wrapAt: wrapLineLen,
textAlign: "center",
box: "rectangle",
boxPadding: textPadding,
...fixWidth?{width: width}:null
}
);
ea.addElementsToView(true);
}

View File

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

View File

@@ -1,9 +1,4 @@
/*
![](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-elements.jpg)
This script will add an encapsulating box around the currently selected elements in Excalidraw.
@@ -13,9 +8,29 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
```javascript
*/
//uncomment if you don't want a prompt for custom padding
//const padding = 10
const padding = parseInt (await utils.inputPrompt("padding?","number","10"));
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
settings = ea.getScriptSettings();
//set default values on first run
if(!settings["Default padding"]) {
settings = {
"Prompt for padding?": true,
"Default padding" : {
value: 10,
description: "Padding between the bounding box of the selected elements, and the box the script creates"
}
};
ea.setScriptSettings(settings);
}
let padding = settings["Default padding"].value;
if(settings["Prompt for padding?"]) {
padding = parseInt (await utils.inputPrompt("padding?","number",padding.toString()));
}
if(isNaN(padding)) {
new Notice("The padding value provided is not a number");
return;

View File

@@ -0,0 +1,15 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-change-shape.jpg)
The script allows you to change the shape of selected Rectangles, Diamonds and Ellipses.
```javascript
*/
const shapes=["ellipse","rectangle","diamond"];
elements = ea.getViewSelectedElements().filter(el=>shapes.contains(el.type));
newShape = await utils.suggester(shapes, shapes);
if(!newShape) return;
elements.forEach(el=>el.type = newShape);
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

View File

@@ -1,9 +1,4 @@
/*
![](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-connect-elements.jpg)
This script will connect two objects with an arrow. If either of the objects are a set of grouped elements (e.g. a text element grouped with an encapsulating rectangle), the script will identify these groups, and connect the arrow to the largest object in the group (assuming you want to connect the arrow to the box around the text element).
@@ -13,23 +8,74 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
settings = ea.getScriptSettings();
//set default values on first run
if(!settings["Starting arrowhead"]) {
settings = {
"Starting arrowhead" : {
value: "none",
valueset: ["none","arrow","triangle","bar","dot"]
},
"Ending arrowhead" : {
value: "triangle",
valueset: ["none","arrow","triangle","bar","dot"]
},
"Line points" : {
value: 1,
description: "Number of line points between start and end"
}
};
ea.setScriptSettings(settings);
}
const arrowStart = settings["Starting arrowhead"].value === "none" ? null : settings["Starting arrowhead"].value;
const arrowEnd = settings["Ending arrowhead"].value === "none" ? null : settings["Ending arrowhead"].value;
const linePoints = Math.floor(settings["Line points"].value);
const elements = ea.getViewSelectedElements();
ea.copyViewElementsToEAforEditing(elements);
const groups = ea.getMaximumGroups(elements);
if(groups.length !== 2) return;
groups = ea.getMaximumGroups(elements);
if(groups.length !== 2) {
//unfortunately getMaxGroups returns duplicated resultset for sticky notes
//needs additional filtering
cleanGroups=[];
idList = [];
for (group of groups) {
keep = true;
for(item of group) if(idList.contains(item.id)) keep = false;
if(keep) {
cleanGroups.push(group);
idList = idList.concat(group.map(el=>el.id))
}
}
if(cleanGroups.length !== 2) return;
groups = cleanGroups;
}
els = [
ea.getLargestElement(groups[0]),
ea.getLargestElement(groups[1])
];
ea.style.strokeColor = els[0].strokeColor;
ea.style.strokeWidth = els[0].strokeWidth;
ea.style.strokeStyle = els[0].strokeStyle;
ea.style.strokeSharpness = els[0].strokeSharpness;
ea.connectObjects(
els[0].id,
null,
els[1].id,
null,
{
endArrowHead: "triangle",
startArrowHead: "dot",
numberOfPoints: 2
endArrowHead: arrowEnd,
startArrowHead: arrowStart,
numberOfPoints: linePoints
}
);
ea.addElementsToView();

View File

@@ -0,0 +1,51 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-textelement-to-transparent-stickynote.png)
Converts selected plain text elements to sticky notes with transparent background and transparent stroke color. Essentially converts text element into a wrappable format.
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
settings = ea.getScriptSettings();
//set default values on first run
if(!settings["Border color"]) {
settings = {
"Border color" : {
value: "#000000",
description: "Any legal HTML color (#000000, rgb, color-name, etc.). Set to 'transparent' for transparent color."
},
"Background color" : {
value: "transparent",
description: "Background color of the sticky note. Set to 'transparent' for transparent color."
},
"Background fill style" : {
value: "solid",
description: "Fill style of the sticky note",
valueset: ["hachure","cross-hatch","solid"]
}
};
ea.setScriptSettings(settings);
}
const strokeColor = settings["Border color"].value;
const backgroundColor = settings["Background color"].value;
const fillStyle = settings["Background fill style"].value;
elements = ea.getViewSelectedElements()
.filter((el)=>(el.type==="text")&&(el.containerId===null));
if(elements.length===0) return;
ea.style.strokeColor = strokeColor;
ea.style.backgroundColor = backgroundColor;
ea.style.fillStyle = fillStyle;
const padding = 6;
elements.forEach((el)=>{
const id = ea.addRect(el.x-padding,el.y-padding,el.width+2*padding,el.height+2*padding);
ea.getElement(id).boundElements=[{type:"text",id:el.id}];
el.containerId = id;
});
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

View File

@@ -18,12 +18,15 @@ app.vault.getFiles().forEach((f)=>
f = Array.from(folders);
folder = await utils.suggester(f,f);
folder = folder??""; //if exiting suggester with ESC
folder = folder === "" ? folder : folder + "/";
elements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
elements.forEach((el)=>{
el.rawText = "[["+folder+el.rawText+"|"+el.rawText+"]]";
el.text = "[["+folder+el.text+"|"+el.text+"]]";
el.originalText = "[["+folder+el.originalText+"|"+el.originalText+"]]";
})
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

View File

@@ -0,0 +1,43 @@
/*
![](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-copy-selected-element-styles-to-global.png)
This script will copy styles of any selected element into Excalidraw's global styles.
After copying the styles of element such as box, text, or arrow using this script, You can then use Excalidraw's box, arrow, and other tools to create several elements with the same style. This is sometimes more convenient than `Copy Styles` and `Paste Styles`, especially when used with the script `Box Each Selected Groups`.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const element = ea.getViewSelectedElement();
const appState = ea.getExcalidrawAPI().getAppState();
if(!element) {
return;
}
appState.currentItemStrokeWidth = element.strokeWidth;
appState.currentItemStrokeStyle = element.strokeStyle;
appState.currentItemStrokeSharpness = element.strokeSharpness;
appState.currentItemRoughness = element.roughness;
appState.currentItemFillStyle = element.fillStyle;
appState.currentItemBackgroundColor = element.backgroundColor;
appState.currentItemStrokeColor = element.strokeColor;
if(element.type === 'text') {
appState.currentItemFontFamily = element.fontFamily;
appState.currentItemFontSize = element.fontSize;
appState.currentItemTextAlign = element.textAlign;
}
if(element.type === 'arrow') {
appState.currentItemStartArrowhead = element.startArrowhead;
appState.currentItemEndArrowhead = element.endArrowhead;
}

View File

@@ -1,289 +1,289 @@
/*
![](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/darken-lighten-background-color.png)
This script darkens the background color of the selected element by 2% at a time.
You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect.
In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.
The color conversion method was copied from [color-convert](https://github.com/Qix-/color-convert).
```javascript
*/
const elements = ea
.getViewSelectedElements()
.filter((el) =>
["rectangle", "ellipse", "diamond", "image"].includes(el.type)
);
ea.copyViewElementsToEAforEditing(elements);
for (const el of ea.getElements()) {
const color = colorNameToHex(el.backgroundColor);
const rgbColor = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
if (rgbColor) {
const r = parseInt(rgbColor[1], 16);
const g = parseInt(rgbColor[2], 16);
const b = parseInt(rgbColor[3], 16);
const originalRgb = [r, g, b];
const hsl = rgbToHsl(originalRgb);
const step = 2;
const newLightness = hsl[2] - step;
if (newLightness > 0) {
hsl[2] = newLightness;
}
const newRgb = hslToRgb(hsl);
el.backgroundColor = "#" + rgbToHexString(newRgb);
}
}
ea.addElementsToView();
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;
}
/*
![](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/darken-lighten-background-color.png)
This script darkens the background color of the selected element by 2% at a time.
You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect.
In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.
The color conversion method was copied from [color-convert](https://github.com/Qix-/color-convert).
```javascript
*/
const elements = ea
.getViewSelectedElements()
.filter((el) =>
["rectangle", "ellipse", "diamond", "image", "line"].includes(el.type)
);
ea.copyViewElementsToEAforEditing(elements);
for (const el of ea.getElements()) {
const color = colorNameToHex(el.backgroundColor);
const rgbColor = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
if (rgbColor) {
const r = parseInt(rgbColor[1], 16);
const g = parseInt(rgbColor[2], 16);
const b = parseInt(rgbColor[3], 16);
const originalRgb = [r, g, b];
const hsl = rgbToHsl(originalRgb);
const step = 2;
const newLightness = hsl[2] - step;
if (newLightness > 0) {
hsl[2] = newLightness;
}
const newRgb = hslToRgb(hsl);
el.backgroundColor = "#" + rgbToHexString(newRgb);
}
}
ea.addElementsToView(false, false);
function rgbToHexString(args) {
const integer =
((Math.round(args[0]) & 0xff) << 16) +
((Math.round(args[1]) & 0xff) << 8) +
(Math.round(args[2]) & 0xff);
const string = integer.toString(16).toUpperCase();
return "000000".substring(string.length) + string;
}
function hslToRgb(hsl) {
const h = hsl[0] / 360;
const s = hsl[1] / 100;
const l = hsl[2] / 100;
let t2;
let t3;
let val;
if (s === 0) {
val = l * 255;
return [val, val, val];
}
if (l < 0.5) {
t2 = l * (1 + s);
} else {
t2 = l + s - l * s;
}
const t1 = 2 * l - t2;
const rgb = [0, 0, 0];
for (let i = 0; i < 3; i++) {
t3 = h + (1 / 3) * -(i - 1);
if (t3 < 0) {
t3++;
}
if (t3 > 1) {
t3--;
}
if (6 * t3 < 1) {
val = t1 + (t2 - t1) * 6 * t3;
} else if (2 * t3 < 1) {
val = t2;
} else if (3 * t3 < 2) {
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
} else {
val = t1;
}
rgb[i] = val * 255;
}
return rgb;
}
function rgbToHsl(rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const min = Math.min(r, g, b);
const max = Math.max(r, g, b);
const delta = max - min;
let h;
let s;
if (max === min) {
h = 0;
} else if (r === max) {
h = (g - b) / delta;
} else if (g === max) {
h = 2 + (b - r) / delta;
} else if (b === max) {
h = 4 + (r - g) / delta;
}
h = Math.min(h * 60, 360);
if (h < 0) {
h += 360;
}
const l = (min + max) / 2;
if (max === min) {
s = 0;
} else if (l <= 0.5) {
s = delta / (max + min);
} else {
s = delta / (2 - max - min);
}
return [h, s * 100, l * 100];
}
function colorNameToHex(color) {
const colors = {
aliceblue: "#f0f8ff",
antiquewhite: "#faebd7",
aqua: "#00ffff",
aquamarine: "#7fffd4",
azure: "#f0ffff",
beige: "#f5f5dc",
bisque: "#ffe4c4",
black: "#000000",
blanchedalmond: "#ffebcd",
blue: "#0000ff",
blueviolet: "#8a2be2",
brown: "#a52a2a",
burlywood: "#deb887",
cadetblue: "#5f9ea0",
chartreuse: "#7fff00",
chocolate: "#d2691e",
coral: "#ff7f50",
cornflowerblue: "#6495ed",
cornsilk: "#fff8dc",
crimson: "#dc143c",
cyan: "#00ffff",
darkblue: "#00008b",
darkcyan: "#008b8b",
darkgoldenrod: "#b8860b",
darkgray: "#a9a9a9",
darkgreen: "#006400",
darkkhaki: "#bdb76b",
darkmagenta: "#8b008b",
darkolivegreen: "#556b2f",
darkorange: "#ff8c00",
darkorchid: "#9932cc",
darkred: "#8b0000",
darksalmon: "#e9967a",
darkseagreen: "#8fbc8f",
darkslateblue: "#483d8b",
darkslategray: "#2f4f4f",
darkturquoise: "#00ced1",
darkviolet: "#9400d3",
deeppink: "#ff1493",
deepskyblue: "#00bfff",
dimgray: "#696969",
dodgerblue: "#1e90ff",
firebrick: "#b22222",
floralwhite: "#fffaf0",
forestgreen: "#228b22",
fuchsia: "#ff00ff",
gainsboro: "#dcdcdc",
ghostwhite: "#f8f8ff",
gold: "#ffd700",
goldenrod: "#daa520",
gray: "#808080",
green: "#008000",
greenyellow: "#adff2f",
honeydew: "#f0fff0",
hotpink: "#ff69b4",
"indianred ": "#cd5c5c",
indigo: "#4b0082",
ivory: "#fffff0",
khaki: "#f0e68c",
lavender: "#e6e6fa",
lavenderblush: "#fff0f5",
lawngreen: "#7cfc00",
lemonchiffon: "#fffacd",
lightblue: "#add8e6",
lightcoral: "#f08080",
lightcyan: "#e0ffff",
lightgoldenrodyellow: "#fafad2",
lightgrey: "#d3d3d3",
lightgreen: "#90ee90",
lightpink: "#ffb6c1",
lightsalmon: "#ffa07a",
lightseagreen: "#20b2aa",
lightskyblue: "#87cefa",
lightslategray: "#778899",
lightsteelblue: "#b0c4de",
lightyellow: "#ffffe0",
lime: "#00ff00",
limegreen: "#32cd32",
linen: "#faf0e6",
magenta: "#ff00ff",
maroon: "#800000",
mediumaquamarine: "#66cdaa",
mediumblue: "#0000cd",
mediumorchid: "#ba55d3",
mediumpurple: "#9370d8",
mediumseagreen: "#3cb371",
mediumslateblue: "#7b68ee",
mediumspringgreen: "#00fa9a",
mediumturquoise: "#48d1cc",
mediumvioletred: "#c71585",
midnightblue: "#191970",
mintcream: "#f5fffa",
mistyrose: "#ffe4e1",
moccasin: "#ffe4b5",
navajowhite: "#ffdead",
navy: "#000080",
oldlace: "#fdf5e6",
olive: "#808000",
olivedrab: "#6b8e23",
orange: "#ffa500",
orangered: "#ff4500",
orchid: "#da70d6",
palegoldenrod: "#eee8aa",
palegreen: "#98fb98",
paleturquoise: "#afeeee",
palevioletred: "#d87093",
papayawhip: "#ffefd5",
peachpuff: "#ffdab9",
peru: "#cd853f",
pink: "#ffc0cb",
plum: "#dda0dd",
powderblue: "#b0e0e6",
purple: "#800080",
rebeccapurple: "#663399",
red: "#ff0000",
rosybrown: "#bc8f8f",
royalblue: "#4169e1",
saddlebrown: "#8b4513",
salmon: "#fa8072",
sandybrown: "#f4a460",
seagreen: "#2e8b57",
seashell: "#fff5ee",
sienna: "#a0522d",
silver: "#c0c0c0",
skyblue: "#87ceeb",
slateblue: "#6a5acd",
slategray: "#708090",
snow: "#fffafa",
springgreen: "#00ff7f",
steelblue: "#4682b4",
tan: "#d2b48c",
teal: "#008080",
thistle: "#d8bfd8",
tomato: "#ff6347",
turquoise: "#40e0d0",
violet: "#ee82ee",
wheat: "#f5deb3",
white: "#ffffff",
whitesmoke: "#f5f5f5",
yellow: "#ffff00",
yellowgreen: "#9acd32",
};
if (typeof colors[color.toLowerCase()] != "undefined")
return colors[color.toLowerCase()];
return color;
}

View File

@@ -16,10 +16,17 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow')) ?
[...result, group[0]] : result, []);
const groupWidths = topGroups
.map((g) =>
g.reduce(
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minLeft: 0, maxRight: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
@@ -39,8 +46,8 @@ const groupWidths = topGroups
}
},
{ minLeft: 0, maxRight: 0 }
)
)
);
})
.map((r) => {
r.width = r.maxRight - r.minLeft;
return r;
@@ -61,15 +68,79 @@ for (var i = 0; i < topGroups.length; i++) {
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
const rectLeft = rect.x;
const rectTop = rect.y;
const rectRight = rect.x + rect.width;
const rectBottom = rect.y + rect.height;
rect.x = rect.x + perRectDistance * j - perRectDistance / 2;
rect.width += perRectDistance;
}
for (var j = 0; j < texts.length; j++) {
const text = texts[j];
text.x = text.x + perRectDistance * j;
const textsWithRect = texts.filter(text => text.x >= rectLeft && text.x <= rectRight
&& text.y >= rectTop && text.y <= rectBottom);
for(const text of textsWithRect) {
text.x = text.x + perRectDistance * j;
}
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
ea.addElementsToView(false, false);
function recalculateStartPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[1][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[1][1];
line.startBinding.gap = 8;
line.startBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.startBinding.gap
);
if(intersectA.length > 0) {
line.points[0] = [0, 0];
for(var i = 1; i<line.points.length; i++) {
line.points[i][0] -= intersectA[0][0] - line.x;
line.points[i][1] -= intersectA[0][1] - line.y;
}
line.x = intersectA[0][0];
line.y = intersectA[0][1];
}
}
function recalculateEndPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[line.points.length-2][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[line.points.length-2][1];
line.endBinding.gap = 8;
line.endBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.endBinding.gap
);
if(intersectA.length > 0) {
line.points[line.points.length - 1] = [intersectA[0][0] - line.x, intersectA[0][1] - line.y];
}
}

View File

@@ -16,10 +16,17 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
const groupWidths = topGroups
.map((g) =>
g.reduce(
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minLeft: 0, maxRight: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
@@ -39,8 +46,8 @@ const groupWidths = topGroups
}
},
{ minLeft: 0, maxRight: 0 }
)
)
);
})
.map((r) => {
r.width = r.maxRight - r.minLeft;
return r;
@@ -52,9 +59,7 @@ for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.x - rha.x);
const texts = topGroups[i]
.filter((el) => el.type === "text")
.sort((lha, rha) => lha.x - rha.x);
const groupWith = groupWidths[i].width;
if (groupWith < maxGroupWidth) {
const distance = maxGroupWidth - groupWith;
@@ -63,14 +68,66 @@ for (var i = 0; i < topGroups.length; i++) {
const rect = rects[j];
rect.x = rect.x + perRectDistance * j;
rect.width += perRectDistance;
}
for (var j = 0; j < texts.length; j++) {
const text = texts[j];
text.x = text.x + perRectDistance * j;
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
ea.addElementsToView(false, false);
function recalculateStartPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[1][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[1][1];
line.startBinding.gap = 8;
line.startBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.startBinding.gap
);
if(intersectA.length > 0) {
line.points[0] = [0, 0];
for(var i = 1; i<line.points.length; i++) {
line.points[i][0] -= intersectA[0][0] - line.x;
line.points[i][1] -= intersectA[0][1] - line.y;
}
line.x = intersectA[0][0];
line.y = intersectA[0][1];
}
}
function recalculateEndPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[line.points.length-2][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[line.points.length-2][1];
line.endBinding.gap = 8;
line.endBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.endBinding.gap
);
if(intersectA.length > 0) {
line.points[line.points.length - 1] = [intersectA[0][0] - line.x, intersectA[0][1] - line.y];
}
}

View File

@@ -16,10 +16,17 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
const groupHeights = topGroups
.map((g) =>
g.reduce(
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minTop: 0, maxBottom: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
@@ -39,8 +46,8 @@ const groupHeights = topGroups
}
},
{ minTop: 0, maxBottom: 0 }
)
)
);
})
.map((r) => {
r.height = r.maxBottom - r.minTop;
return r;
@@ -61,15 +68,79 @@ for (var i = 0; i < topGroups.length; i++) {
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
const rectLeft = rect.x;
const rectTop = rect.y;
const rectRight = rect.x + rect.width;
const rectBottom = rect.y + rect.height;
rect.y = rect.y + perRectDistance * j - perRectDistance / 2;
rect.height += perRectDistance;
}
for (var j = 0; j < texts.length; j++) {
const text = texts[j];
text.y = text.y + perRectDistance * j;
const textsWithRect = texts.filter(text => text.x >= rectLeft && text.x <= rectRight
&& text.y >= rectTop && text.y <= rectBottom);
for(const text of textsWithRect) {
text.y = text.y + perRectDistance * j;
}
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
ea.addElementsToView(false, false);
function recalculateStartPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[1][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[1][1];
line.startBinding.gap = 8;
line.startBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.startBinding.gap
);
if(intersectA.length > 0) {
line.points[0] = [0, 0];
for(var i = 1; i<line.points.length; i++) {
line.points[i][0] -= intersectA[0][0] - line.x;
line.points[i][1] -= intersectA[0][1] - line.y;
}
line.x = intersectA[0][0];
line.y = intersectA[0][1];
}
}
function recalculateEndPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[line.points.length-2][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[line.points.length-2][1];
line.endBinding.gap = 8;
line.endBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.endBinding.gap
);
if(intersectA.length > 0) {
line.points[line.points.length - 1] = [intersectA[0][0] - line.x, intersectA[0][1] - line.y];
}
}

View File

@@ -13,10 +13,18 @@ This script expands the height of the selected rectangles until they are all the
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const allLines = ea.getViewElements().filter(el => el.type === 'arrow' || el.type === 'line');
const allIndividualArrows = ea.getMaximumGroups(ea.getViewElements())
.reduce((result, group) => (group.length === 1 && (group[0].type === 'arrow' || group[0].type === 'line')) ?
[...result, group[0]] : result, []);
const groupHeights = topGroups
.map((g) =>
g.reduce(
.map((g) => {
if(g.length === 1 && (g[0].type === 'arrow' || g[0].type === 'line')) {
// ignore individual lines
return { minTop: 0, maxBottom: 0 };
}
return g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
@@ -36,8 +44,8 @@ const groupHeights = topGroups
}
},
{ minTop: 0, maxBottom: 0 }
)
)
);
})
.map((r) => {
r.height = r.maxBottom - r.minTop;
return r;
@@ -49,9 +57,7 @@ for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.y - rha.y);
const texts = topGroups[i]
.filter((el) => el.type === "text")
.sort((lha, rha) => lha.y - rha.y);
const groupWith = groupHeights[i].height;
if (groupWith < maxGroupHeight) {
const distance = maxGroupHeight - groupWith;
@@ -60,14 +66,66 @@ for (var i = 0; i < topGroups.length; i++) {
const rect = rects[j];
rect.y = rect.y + perRectDistance * j;
rect.height += perRectDistance;
}
for (var j = 0; j < texts.length; j++) {
const text = texts[j];
text.y = text.y + perRectDistance * j;
// recalculate the position of the points
const startBindingLines = allIndividualArrows.filter(el => (el.startBinding||{}).elementId === rect.id);
for(startBindingLine of startBindingLines) {
recalculateStartPointOfLine(startBindingLine, rect);
}
const endBindingLines = allIndividualArrows.filter(el => (el.endBinding||{}).elementId === rect.id);
for(endBindingLine of endBindingLines) {
recalculateEndPointOfLine(endBindingLine, rect);
}
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
ea.addElementsToView(false, false);
function recalculateStartPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[1][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[1][1];
line.startBinding.gap = 8;
line.startBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.startBinding.gap
);
if(intersectA.length > 0) {
line.points[0] = [0, 0];
for(var i = 1; i<line.points.length; i++) {
line.points[i][0] -= intersectA[0][0] - line.x;
line.points[i][1] -= intersectA[0][1] - line.y;
}
line.x = intersectA[0][0];
line.y = intersectA[0][1];
}
}
function recalculateEndPointOfLine(line, el) {
const aX = el.x + el.width/2;
const bX = line.x + line.points[line.points.length-2][0];
const aY = el.y + el.height/2;
const bY = line.y + line.points[line.points.length-2][1];
line.endBinding.gap = 8;
line.endBinding.focus = 0;
const intersectA = ea.intersectElementWithLine(
el,
[bX, bY],
[aX, aY],
line.endBinding.gap
);
if(intersectA.length > 0) {
line.points[line.points.length - 1] = [intersectA[0][0] - line.x, intersectA[0][1] - line.y];
}
}

View File

@@ -1,37 +1,38 @@
/*
![](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-fix-space-demo.png)
The script arranges the selected elements horizontally with a fixed spacing.
When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.
```javascript
*/
const spacing = parseInt (await utils.inputPrompt("spacing?","number","8"));
if(isNaN(spacing)) {
return;
}
const elements=ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const groups = topGroups.sort((lha,rha) => lha[0].x - rha[0].x);
for(var i=0; i<groups.length; i++) {
if(i > 0) {
const preGroup = groups[i-1];
const curGroup = groups[i];
const preRight = Math.max(...preGroup.map(el => el.x + el.width));
const curLeft = Math.min(...curGroup.map(el => el.x));
const distance = curLeft - preRight - spacing;
for(const curEl of curGroup) {
curEl.x = curEl.x - distance;
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fix-space-demo.png)
The script arranges the selected elements horizontally with a fixed spacing.
When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.
```javascript
*/
const spacing = parseInt (await utils.inputPrompt("spacing?","number","8"));
if(isNaN(spacing)) {
return;
}
const elements=ea.getViewSelectedElements()
.filter(els => !(els.length === 1 && els[0].type ==="arrow")); // ignore individual arrows
const topGroups = ea.getMaximumGroups(elements);
const groups = topGroups.sort((lha,rha) => lha[0].x - rha[0].x);
for(var i=0; i<groups.length; i++) {
if(i > 0) {
const preGroup = groups[i-1];
const curGroup = groups[i];
const preRight = Math.max(...preGroup.map(el => el.x + el.width));
const curLeft = Math.min(...curGroup.map(el => el.x));
const distance = curLeft - preRight - spacing;
for(const curEl of curGroup) {
curEl.x = curEl.x - distance;
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);

View File

@@ -1,37 +1,38 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-vertical-distance.png)
The script arranges the selected elements vertically with a fixed spacing.
When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.
```javascript
*/
const spacing = parseInt (await utils.inputPrompt("spacing?","number","8"));
if(isNaN(spacing)) {
return;
}
const elements=ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const groups = topGroups.sort((lha,rha) => lha[0].y - rha[0].y);
for(var i=0; i<groups.length; i++) {
if(i > 0) {
const preGroup = groups[i-1];
const curGroup = groups[i];
const preBottom = Math.max(...preGroup.map(el => el.y + el.height));
const curTop = Math.min(...curGroup.map(el => el.y));
const distance = curTop - preBottom - spacing;
for(const curEl of curGroup) {
curEl.y = curEl.y - distance;
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-vertical-distance.png)
The script arranges the selected elements vertically with a fixed spacing.
When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.
```javascript
*/
const spacing = parseInt (await utils.inputPrompt("spacing?","number","8"));
if(isNaN(spacing)) {
return;
}
const elements=ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements)
.filter(els => !(els.length === 1 && els[0].type ==="arrow")); // ignore individual arrows
const groups = topGroups.sort((lha,rha) => lha[0].y - rha[0].y);
for(var i=0; i<groups.length; i++) {
if(i > 0) {
const preGroup = groups[i-1];
const curGroup = groups[i];
const preBottom = Math.max(...preGroup.map(el => el.y + el.height));
const curTop = Math.min(...curGroup.map(el => el.y));
const distance = curTop - preBottom - spacing;
for(const curEl of curGroup) {
curEl.y = curEl.y - distance;
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView(false, false);

View File

@@ -1,289 +1,289 @@
/*
![](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/darken-lighten-background-color.png)
This script lightens the background color of the selected element by 2% at a time.
You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect.
In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.
The color conversion method was copied from [color-convert](https://github.com/Qix-/color-convert).
```javascript
*/
const elements = ea
.getViewSelectedElements()
.filter((el) =>
["rectangle", "ellipse", "diamond", "image"].includes(el.type)
);
ea.copyViewElementsToEAforEditing(elements);
for (const el of ea.getElements()) {
const color = colorNameToHex(el.backgroundColor);
const rgbColor = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
if (rgbColor) {
const r = parseInt(rgbColor[1], 16);
const g = parseInt(rgbColor[2], 16);
const b = parseInt(rgbColor[3], 16);
const originalRgb = [r, g, b];
const hsl = rgbToHsl(originalRgb);
const step = 2;
const newLightness = hsl[2] + step;
if (newLightness < 100) {
hsl[2] = newLightness;
}
const newRgb = hslToRgb(hsl);
el.backgroundColor = "#" + rgbToHexString(newRgb);
}
}
ea.addElementsToView();
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;
}
/*
![](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/darken-lighten-background-color.png)
This script lightens the background color of the selected element by 2% at a time.
You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect.
In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.
The color conversion method was copied from [color-convert](https://github.com/Qix-/color-convert).
```javascript
*/
const elements = ea
.getViewSelectedElements()
.filter((el) =>
["rectangle", "ellipse", "diamond", "image", "line"].includes(el.type)
);
ea.copyViewElementsToEAforEditing(elements);
for (const el of ea.getElements()) {
const color = colorNameToHex(el.backgroundColor);
const rgbColor = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
if (rgbColor) {
const r = parseInt(rgbColor[1], 16);
const g = parseInt(rgbColor[2], 16);
const b = parseInt(rgbColor[3], 16);
const originalRgb = [r, g, b];
const hsl = rgbToHsl(originalRgb);
const step = 2;
const newLightness = hsl[2] + step;
if (newLightness < 100) {
hsl[2] = newLightness;
}
const newRgb = hslToRgb(hsl);
el.backgroundColor = "#" + rgbToHexString(newRgb);
}
}
ea.addElementsToView(false, false);
function rgbToHexString(args) {
const integer =
((Math.round(args[0]) & 0xff) << 16) +
((Math.round(args[1]) & 0xff) << 8) +
(Math.round(args[2]) & 0xff);
const string = integer.toString(16).toUpperCase();
return "000000".substring(string.length) + string;
}
function hslToRgb(hsl) {
const h = hsl[0] / 360;
const s = hsl[1] / 100;
const l = hsl[2] / 100;
let t2;
let t3;
let val;
if (s === 0) {
val = l * 255;
return [val, val, val];
}
if (l < 0.5) {
t2 = l * (1 + s);
} else {
t2 = l + s - l * s;
}
const t1 = 2 * l - t2;
const rgb = [0, 0, 0];
for (let i = 0; i < 3; i++) {
t3 = h + (1 / 3) * -(i - 1);
if (t3 < 0) {
t3++;
}
if (t3 > 1) {
t3--;
}
if (6 * t3 < 1) {
val = t1 + (t2 - t1) * 6 * t3;
} else if (2 * t3 < 1) {
val = t2;
} else if (3 * t3 < 2) {
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
} else {
val = t1;
}
rgb[i] = val * 255;
}
return rgb;
}
function rgbToHsl(rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const min = Math.min(r, g, b);
const max = Math.max(r, g, b);
const delta = max - min;
let h;
let s;
if (max === min) {
h = 0;
} else if (r === max) {
h = (g - b) / delta;
} else if (g === max) {
h = 2 + (b - r) / delta;
} else if (b === max) {
h = 4 + (r - g) / delta;
}
h = Math.min(h * 60, 360);
if (h < 0) {
h += 360;
}
const l = (min + max) / 2;
if (max === min) {
s = 0;
} else if (l <= 0.5) {
s = delta / (max + min);
} else {
s = delta / (2 - max - min);
}
return [h, s * 100, l * 100];
}
function colorNameToHex(color) {
const colors = {
aliceblue: "#f0f8ff",
antiquewhite: "#faebd7",
aqua: "#00ffff",
aquamarine: "#7fffd4",
azure: "#f0ffff",
beige: "#f5f5dc",
bisque: "#ffe4c4",
black: "#000000",
blanchedalmond: "#ffebcd",
blue: "#0000ff",
blueviolet: "#8a2be2",
brown: "#a52a2a",
burlywood: "#deb887",
cadetblue: "#5f9ea0",
chartreuse: "#7fff00",
chocolate: "#d2691e",
coral: "#ff7f50",
cornflowerblue: "#6495ed",
cornsilk: "#fff8dc",
crimson: "#dc143c",
cyan: "#00ffff",
darkblue: "#00008b",
darkcyan: "#008b8b",
darkgoldenrod: "#b8860b",
darkgray: "#a9a9a9",
darkgreen: "#006400",
darkkhaki: "#bdb76b",
darkmagenta: "#8b008b",
darkolivegreen: "#556b2f",
darkorange: "#ff8c00",
darkorchid: "#9932cc",
darkred: "#8b0000",
darksalmon: "#e9967a",
darkseagreen: "#8fbc8f",
darkslateblue: "#483d8b",
darkslategray: "#2f4f4f",
darkturquoise: "#00ced1",
darkviolet: "#9400d3",
deeppink: "#ff1493",
deepskyblue: "#00bfff",
dimgray: "#696969",
dodgerblue: "#1e90ff",
firebrick: "#b22222",
floralwhite: "#fffaf0",
forestgreen: "#228b22",
fuchsia: "#ff00ff",
gainsboro: "#dcdcdc",
ghostwhite: "#f8f8ff",
gold: "#ffd700",
goldenrod: "#daa520",
gray: "#808080",
green: "#008000",
greenyellow: "#adff2f",
honeydew: "#f0fff0",
hotpink: "#ff69b4",
"indianred ": "#cd5c5c",
indigo: "#4b0082",
ivory: "#fffff0",
khaki: "#f0e68c",
lavender: "#e6e6fa",
lavenderblush: "#fff0f5",
lawngreen: "#7cfc00",
lemonchiffon: "#fffacd",
lightblue: "#add8e6",
lightcoral: "#f08080",
lightcyan: "#e0ffff",
lightgoldenrodyellow: "#fafad2",
lightgrey: "#d3d3d3",
lightgreen: "#90ee90",
lightpink: "#ffb6c1",
lightsalmon: "#ffa07a",
lightseagreen: "#20b2aa",
lightskyblue: "#87cefa",
lightslategray: "#778899",
lightsteelblue: "#b0c4de",
lightyellow: "#ffffe0",
lime: "#00ff00",
limegreen: "#32cd32",
linen: "#faf0e6",
magenta: "#ff00ff",
maroon: "#800000",
mediumaquamarine: "#66cdaa",
mediumblue: "#0000cd",
mediumorchid: "#ba55d3",
mediumpurple: "#9370d8",
mediumseagreen: "#3cb371",
mediumslateblue: "#7b68ee",
mediumspringgreen: "#00fa9a",
mediumturquoise: "#48d1cc",
mediumvioletred: "#c71585",
midnightblue: "#191970",
mintcream: "#f5fffa",
mistyrose: "#ffe4e1",
moccasin: "#ffe4b5",
navajowhite: "#ffdead",
navy: "#000080",
oldlace: "#fdf5e6",
olive: "#808000",
olivedrab: "#6b8e23",
orange: "#ffa500",
orangered: "#ff4500",
orchid: "#da70d6",
palegoldenrod: "#eee8aa",
palegreen: "#98fb98",
paleturquoise: "#afeeee",
palevioletred: "#d87093",
papayawhip: "#ffefd5",
peachpuff: "#ffdab9",
peru: "#cd853f",
pink: "#ffc0cb",
plum: "#dda0dd",
powderblue: "#b0e0e6",
purple: "#800080",
rebeccapurple: "#663399",
red: "#ff0000",
rosybrown: "#bc8f8f",
royalblue: "#4169e1",
saddlebrown: "#8b4513",
salmon: "#fa8072",
sandybrown: "#f4a460",
seagreen: "#2e8b57",
seashell: "#fff5ee",
sienna: "#a0522d",
silver: "#c0c0c0",
skyblue: "#87ceeb",
slateblue: "#6a5acd",
slategray: "#708090",
snow: "#fffafa",
springgreen: "#00ff7f",
steelblue: "#4682b4",
tan: "#d2b48c",
teal: "#008080",
thistle: "#d8bfd8",
tomato: "#ff6347",
turquoise: "#40e0d0",
violet: "#ee82ee",
wheat: "#f5deb3",
white: "#ffffff",
whitesmoke: "#f5f5f5",
yellow: "#ffff00",
yellowgreen: "#9acd32",
};
if (typeof colors[color.toLowerCase()] != "undefined")
return colors[color.toLowerCase()];
return color;
}

View File

@@ -3,6 +3,8 @@
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-modify-background-color-opacity.png)
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.
@@ -33,7 +35,7 @@ ea.getElements().forEach((el)=>{
}
}
});
ea.addElementsToView();
ea.addElementsToView(false, false);
function colorNameToHex(color) {
const colors = {

View File

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

View File

@@ -1,16 +1,12 @@
/*
![](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-ocr.jpg)
THIS SCRIPT REQUIRES EXCALIDRAW 1.5.15
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
⚠ Don't forget to paste your token into the script after the first run. ⚠
I recommend also installing the [Transfer TextElements to Excalidraw markdown metadata](Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md) script as well.
The script is based on [@schlundd](https://github.com/schlundd)'s [Obsidian-OCR-Plugin](https://github.com/schlundd/obsidian-ocr-plugin)
@@ -20,11 +16,21 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
```javascript
*/
let token = ""; //paste token in-between the quotation marks "xxxxxxxxxx"
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
let token = ea.getScriptSettings().token.value??ea.getScriptSettings().token;
const BASE_URL = "https://ocr.taskbone.com";
//convert setting to 1.5.21 format
if(token && !ea.getScriptSettings().token.value) {
ea.setScriptSettings({token: {value: token, hidden: true}});
}
//get new token if token was not provided
if (token==="") {
if (!token) {
const tokenResponse = await fetch(
BASE_URL + "/get-new-token", {
method: 'post'
@@ -32,9 +38,7 @@ if (token==="") {
if (tokenResponse.status === 200) {
jsonResponse = await tokenResponse.json();
token = jsonResponse.token;
navigator.clipboard.writeText(token);
notice("Please update the ScriptEngine script with the Token.\n\nToken is on the clipboard and in Developer Console.");
console.log({token});
ea.setScriptSettings({token: {value: token, hidden: true}});
} else {
notice(`Taskbone OCR Error: ${tokenResponse.status}\nPlease try again later.`);
return;

View File

@@ -14,13 +14,19 @@ Open the script you are interested in and save it to your Obsidian Vault includi
## List of available scripts
|Title|Description|Icon|Contributor|
|----|----|----|----|
|[Add Connector Point](Add%20Connector%20Point.md)|This script will add a small circle to the top left of each text element in the selection and add the text and the "bullet point" into a group.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-bullet-point.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Add Link and Open Page](Add%20Link%20and%20Open%20Page.md)|Prompt for a file in the vault. Add a link above or below (based on settings) the selected element, to the selected file. If no file is selected then the script creates a new file following the default filename defined for excalidraw embeds.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-link-and-open.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Add Next Step in Process](Add%20Next%20Step%20in%20Process.md)|This script will prompt you for the title of the process step, then will create a stick note with the text. If an element is selected then the script will connect this new step with an arrow to the previous step (the selected element). If no element is selected, then the script assumes this is the first step in the process and will only output the sticky note with the text that was entered.|![]https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-process-step.jpg|[@zsviczian](https://github.com/zsviczian)|
|[Box Each Selected Groups](Box%20Each%20Selected%20Groups.md)|This script will add encapsulating boxes around each of the currently selected groups in Excalidraw.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-each-selected-groups.png)|[@1-2-3](https://github.com/1-2-3)|
|[Box Selected Elements](Box%20Selected%20Elements.md)|This script will add an encapsulating box around the currently selected elements in Excalidraw.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-elements.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Bullet Point](Bullet%20Point.md)|This script will add a small circle to the top left of each text element in the selection and add the text and the "bullet point" into a group.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-bullet-point.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Connect elements](Connect%20elements.md)|This script will connect two objects with an arrow. If either of the objects are a set of grouped elements (e.g. a text element grouped with an encapsulating rectangle), the script will identify these groups, and connect the arrow to the largest object in the group (assuming you want to connect the arrow to the box around the text element).|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-connect-elements.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Change shape of selected elements](Change%20shape%20of%20selected%20elements.md)|The script allows you to change the shape of selected Rectangles, Diamonds and Ellipses|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-change-shape.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Convert selected text elements to sticky notes](Convert%20selected%20text%20elements%20to%20sticky%20notes.md)|Converts selected plain text elements to sticky notes with transparent background and transparent stroke color. Essentially converts text element into a wrappable format.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-textelement-to-transparent-stickynote.png)|[@zsviczian](https://github.com/zsviczian)|
|[Convert text to link with folder and alias](Convert%20text%20to%20link%20with%20folder%20and%20alias.md)|Converts text elements to links pointing to a file in a selected folder and with the alias set as the original text. The script will prompt the user to select an existing folder from the vault.|`original text` => `[[selected folder/original text\|original text]]`|[@zsviczian](https://github.com/zsviczian)|
|[Copy Selected Element Styles to Global](Copy%20Selected%20Element%20Styles%20to%20Global)|This script will copy styles of any selected element into Excalidraw's global styles.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-copy-selected-element-styles-to-global.png)|[@1-2-3](https://github.com/1-2-3)|
|[Create new markdown file and embed into active drawing](Create%20new%20markdown%20file%20and%20embed%20into%20active%20drawing.md)|The script will prompt you for a filename, then create a new markdown document with the file name provided, open the new markdown document in an adjacent pane, and embed the markdown document into the active Excalidraw drawing.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-create-and-embed-new-markdown-file.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Darken background color](Darken%20background%20color.md)|This script darkens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect. In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png)|[@1-2-3](https://github.com/1-2-3)|
|[Dimensions](Dimensions.md)|Currently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-dimensions.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Dimensions](Set%20Dimensions.md)|Currently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-dimensions.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Elbow connectors](Elbow%20connectors.md)|This script converts the selected connectors to elbows.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/elbow-connectors.png)|[@1-2-3](https://github.com/1-2-3)|
|[Expand rectangles horizontally keep text centered](Expand%20rectangles%20horizontally%20keep%20text20%centered.md)|This script expands the width of the selected rectangles until they are all the same width and keep the text centered.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)|[@1-2-3](https://github.com/1-2-3)|
|[Expand rectangles horizontally](Expand%20rectangles%20horizontally.md)|This script expands the width of the selected rectangles until they are all the same width.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)|[@1-2-3](https://github.com/1-2-3)|
@@ -28,15 +34,16 @@ Open the script you are interested in and save it to your Obsidian Vault includi
|[Expand rectangles vertically](Expand%20rectangles%20vertically.md)|This script expands the height of the selected rectangles until they are all the same height.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)|[@1-2-3](https://github.com/1-2-3)|
|[Fixed spacing](Fixed%20spacing.md)|The script arranges the selected elements horizontally with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fix-space-demo.png)|[@1-2-3](https://github.com/1-2-3)|
|[Fixed vertical distance](Fixed%20vertical%20distance.md)|The script arranges the selected elements vertically with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-vertical-distance.png)|[@1-2-3](https://github.com/1-2-3)|
|[Font Family](Font%20Family.md)|Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-font-family.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Grid](Grid.md)|The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Font Family](Set%20Font%20Family.md)|Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-font-family.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Grid](Set%20Grid.md)|The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Lighten background color](Lighten%20background%20color.md)|This script lightens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect.In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png)|[@1-2-3](https://github.com/1-2-3)|
|[Modify background color opacity](Modify%20background%20color%20opacity.md)|This script changes the opacity of the background color of the selected boxes. The default background color in Excalidraw is so dark that the text is hard to read. You can lighten the color a bit by setting transparency. And you can tweak the transparency over and over again until you're happy with it. Although excalidraw has the opacity option in its native property Settings, it also changes the transparency of the border. Use this script to change only the opacity of the background color without affecting the border.||[@1-2-3](https://github.com/1-2-3)|
|[Modify stroke width of selected elements](Modify%20stroke%20width%20of%20selected%20elements.md)|This script will set the stroke width of selected elements. This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-stroke-width.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Modify background color opacity](Modify%20background%20color%20opacity.md)|This script changes the opacity of the background color of the selected boxes. The default background color in Excalidraw is so dark that the text is hard to read. You can lighten the color a bit by setting transparency. And you can tweak the transparency over and over again until you're happy with it. Although excalidraw has the opacity option in its native property Settings, it also changes the transparency of the border. Use this script to change only the opacity of the background color without affecting the border.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-modify-background-color-opacity.png)|[@1-2-3](https://github.com/1-2-3)|
|[Set stroke width of selected elements](Set%20Stroke%20Width%20of%20Selected%20Elements.md)|This script will set the stroke width of selected elements. This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-stroke-width.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Normalize Selected Arrows](Normalize%20Selected%20Arrows.md)|This script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-normalize-selected-arrows.png)|[@1-2-3](https://github.com/1-2-3)|
|[OCR - Optical Character Recognition](OCR%20-%20Optical%20Character%20Recognition.md)|The script will 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and 2) will add the text to your drawing as a text element.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-ocr.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Reverse arrows](Reverse%20arrows.md)|Reverse the direction of **arrows** within the scope of selected elements.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-reverse-arrow.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Link Alias](Set20%Link20%Alias.md)|Iterates all of the links in the selected TextElements and prompts the user to set or modify the alias for each link found.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-set-link-alias.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Split text by lines](Split%20text%20by%20lines.md)|Split lines of text into separate text elements for easier reorganization|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-split-lines.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Text Align](Text%20Align.md)|Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-align.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Text Alignment](Set%20Text%20Alignment.md)|Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-align.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Transfer TextElements to Excalidraw markdown metadata](Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md)|The script will delete the selected text elements from the canvas and will copy the text from these text elements into the Excalidraw markdown file as metadata. This means, that the text will no longer be visible in the drawing, however you will be able to search for the text in Obsidian and find the drawing containing this image.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-to-metadata.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Zoom to Fit Selected Elements](Zoom%20to%20Fit%20Selected%20Elements.md)|Similar to Excalidraw standard SHIFT+2 feature: Zoom to fit selected elements, but with the ability to zoom to 1000%. Inspiration: [#272](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/272)||[@zsviczian](https://github.com/zsviczian)|

View File

@@ -1,8 +1,4 @@
/*
![](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-font-family.jpg)
Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.

View File

@@ -1,9 +1,4 @@
/*
![](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-grid.jpg)
The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.

View File

@@ -1,9 +1,4 @@
/*
![](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-stroke-width.jpg)
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.

View File

@@ -1,8 +1,4 @@
/*
![](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-text-align.jpg)
Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.

View File

@@ -25,13 +25,18 @@ I would love to include your contribution in the script library. If you have a s
---
# List of available scripts
- [[#Add Connector Point]]
- [[#Add Link and Open Page]]
- [[#Add Next Step in Process]]
- [[#Box Each Selected Groups]]
- [[#Box Selected Elements]]
- [[#Bullet Point]]
- [[#Change shape of selected elements]]
- [[#Connect elements]]
- [[#Convert selected text elements to sticky notes]]
- [[#Convert text to link with folder and alias]]
- [[#Copy Selected Element Styles to Global]]
- [[#Create new markdown file and embed into active drawing]]
- [[#Darken background color]]
- [[#Dimensions]]
- [[#Elbow connectors]]
- [[#Expand rectangles horizontally keep text centered]]
- [[#Expand rectangles horizontally]]
@@ -39,19 +44,44 @@ I would love to include your contribution in the script library. If you have a s
- [[#Expand rectangles vertically]]
- [[#Fixed spacing]]
- [[#Fixed vertical distance]]
- [[#Font Family]]
- [[#Grid]]
- [[#Lighten background color]]
- [[#Modify background color opacity]]
- [[#Modify stroke width of selected elements]]
- [[#Normalize Selected Arrows]]
- [[#OCR - Optical Character Recognition]]
- [[#Reverse arrows]]
- [[#Set Dimensions]]
- [[#Set Font Family]]
- [[#Set Grid]]
- [[#Set Link Alias]]
- [[#Set Stroke Width of Selected Elements]]
- [[#Set Text Alignment]]
- [[#Split text by lines]]
- [[#Text Align]]
- [[#Transfer TextElements to Excalidraw markdown metadata]]
- [[#Zoom to Fit Selected Elements]]
## Add Connector Point
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Connector%20Point.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Add%20Connector%20Point.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will add a small circle to the top left of each text element in the selection and add the text and the "connector point" to a group. You can use the connector points to link text elements with an arrow (in for example a Wardley Map).<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-bullet-point.jpg'></td></tr></table>
## Add Link and Open Page
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Link%20and%20Open%20Page.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Add%20Link%20and%20Open%20Page.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Prompt for a file in the vault. Add a link above or below (based on settings) the selected element, to the selected file. If no file is selected then the script creates a new file following the default filename defined for excalidraw embeds.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-link-and-open.jpg'></td></tr></table>
## Add Next Step in Process
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Next%20Step%20in%20Process.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Add%20Next%20Step%20in%20Process.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will prompt you for the title of the process step, then will create a stick note with the text. If an element is selected then the script will connect this new step with an arrow to the previous step (the selected element). If no element is selected, then the script assumes this is the first step in the process and will only output the sticky note with the text that was entered.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-process-step.jpg'></td></tr></table>
## Box Each Selected Groups
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Box%20Each%20Selected%20Groups.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/Box%20Each%20Selected%20Groups.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will add encapsulating boxes around each of the currently selected groups in Excalidraw.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-each-selected-groups.png'></td></tr></table>
## Box Selected Elements
```excalidraw-script-install
@@ -59,11 +89,11 @@ 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/Box%20Selected%20Elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will add an encapsulating box around the currently selected elements in Excalidraw.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-elements.jpg'></td></tr></table>
## Bullet Point
## Change shape of selected elements
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Bullet%20Point.md
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Change%20shape%20of%20selected%20elements.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/Bullet%20Point.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will add a small circle to the top left of each text element in the selection and add the text and the "bullet point" into a group.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-bullet-point.jpg'></td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Change%20shape%20of%20selected%20elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script allows you to change the shape of selected Rectangles, Diamonds and Ellipses.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-change-shape.jpg'></td></tr></table>
## Connect elements
```excalidraw-script-install
@@ -71,12 +101,24 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Connect%20elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will connect two objects with an arrow. If either of the objects are a set of grouped elements (e.g. a text element grouped with an encapsulating rectangle), the script will identify these groups, and connect the arrow to the largest object in the group (assuming you want to connect the arrow to the box around the text element).<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-connect-elements.jpg'></td></tr></table>
## Convert selected text elements to sticky notes
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20selected%20text%20elements%20to%20sticky%20notes.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Convert%20selected%20text%20elements%20to%20sticky%20notes.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Converts selected plain text elements to sticky notes with transparent background and transparent stroke color (default setting, can be changed in plugin settings). Essentially converts text element into a wrappable format.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-textelement-to-transparent-stickynote.png'></td></tr></table>
## Convert text to link with folder and alias
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20text%20to%20link%20with%20folder%20and%20alias.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Convert%20text%20to%20link%20with%20folder%20and%20alias.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Converts text elements to links pointing to a file in a selected folder and with the alias set as the original text. The script will prompt the user to select an existing folder from the vault.<br><code>original text</code> - <code>[[selected folder/original text|original text]]</code></td></tr></table>
## Copy Selected Element Styles to Global
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Copy%20Selected%20Element%20Styles%20to%20Global.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/Copy%20Selected%20Element%20Styles%20to%20Global.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will copy styles of any selected element into Excalidraw's global styles.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-copy-selected-element-styles-to-global.png'></td></tr></table>
## Create new markdown file and embed into active drawing
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Create%20new%20markdown%20file%20and%20embed%20into%20active%20drawing.md
@@ -89,12 +131,6 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Darken%20background%20color.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script darkens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect. In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png'></td></tr></table>
## Dimensions
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Dimensions.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/Dimensions.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Currently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-dimensions.jpg'></td></tr></table>
## Elbow connectors
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Elbow%20connectors.md
@@ -137,18 +173,6 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Fixed%20vertical%20distance.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script arranges the selected elements vertically with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-vertical-distance.png'></td></tr></table>
## Font Family
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Font%20Family.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/Font%20Family.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-font-family.jpg'></td></tr></table>
## Grid
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Grid.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/Grid.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid.jpg'></td></tr></table>
## Lighten background color
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Lighten%20background%20color.md
@@ -159,19 +183,19 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Modify%20background%20color%20opacity.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/Modify%20background%20color%20opacity.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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.</td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Modify%20background%20color%20opacity.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-modify-background-color-opacity.png'></td></tr></table>
## Modify stroke width of selected elements
## Normalize Selected Arrows
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Modify%20stroke%20width%20of%20selected%20elements.md
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Normalize%20Selected%20Arrows
```
<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/Modify%20stroke%20width%20of%20selected%20elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-stroke-width.jpg'></td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Normalize%20Selected%20Arrows'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-normalize-selected-arrows.png'></td></tr></table>
## OCR - Optical Character Recognition
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/OCR%20-%20Optical%20Character%20Recognition.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/OCR%20-%20Optical%20Character%20Recognition.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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>
<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>
## Reverse arrows
```excalidraw-script-install
@@ -179,24 +203,48 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Reverse%20arrows.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Reverse the direction of **arrows** within the scope of selected elements.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-reverse-arrow.jpg'></td></tr></table>
## Set Dimensions
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Dimensions.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20Dimensions.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Currently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-dimensions.jpg'></td></tr></table>
## Set Font Family
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Font%20Family.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20Font%20Family.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-font-family.jpg'></td></tr></table>
## Set Grid
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Grid.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20Grid.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid.jpg'></td></tr></table>
## Set Link Alias
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Link%20Alias.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20Link%20Alias.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Iterates all of the links in the selected TextElements and prompts the user to set or modify the alias for each link found.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-set-link-alias.jpg'></td></tr></table>
## Set Stroke Width of Selected Elements
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Stroke%20Width%20of%20Selected%20Elements.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20Stroke%20Width%20of%20Selected%20Elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-stroke-width.jpg'></td></tr></table>
## Set Text Alignment
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Text%20Alignment.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20Text%20Alignment.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-align.jpg'></td></tr></table>
## Split text by lines
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Split%20text%20by%20lines.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/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>
## Text Align
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Text%20Align.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/Text%20Align.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-align.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
@@ -207,4 +255,4 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.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/Zoom%20to%20Fit%20Selected%20Elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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)</td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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)</td></tr></table>

BIN
images/ComplexSettings.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
images/SimpleSettings.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

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

View File

@@ -12,36 +12,36 @@
"author": "",
"license": "MIT",
"dependencies": {
"@zsviczian/excalidraw": "0.10.0-obsidian-33",
"monkey-around": "^2.2.0",
"@zsviczian/excalidraw": "0.10.0-obsidian-37",
"monkey-around": "^2.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "^1.1.5",
"roughjs": "4.4.1"
"react-scripts": "^5.0.0",
"roughjs": "^4.5.2"
},
"devDependencies": {
"@babel/core": "^7.15.5",
"@babel/preset-env": "^7.15.6",
"@babel/preset-react": "^7.14.5",
"@babel/core": "^7.16.12",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^21.0.0",
"@rollup/plugin-node-resolve": "^13.0.5",
"@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.5",
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.1.3",
"@rollup/plugin-replace": "^3.0.1",
"@rollup/plugin-typescript": "^8.3.0",
"@types/js-beautify": "^1.13.3",
"@types/node": "^15.12.4",
"@types/react-dom": "^17.0.9",
"@types/react-dom": "^17.0.11",
"cross-env": "^7.0.3",
"html2canvas": "^1.3.2",
"nanoid": "^3.1.23",
"obsidian": "^0.13.11",
"rollup": "^2.52.3",
"rollup-plugin-visualizer": "^5.5.2",
"html2canvas": "^1.4.0",
"nanoid": "^3.1.31",
"obsidian": "^0.13.21",
"rollup": "^2.66.0",
"rollup-plugin-visualizer": "^5.5.4",
"tslib": "^2.3.1",
"typescript": "^4.4.3",
"typescript": "^4.5.5",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-prettier": "3.3.1",
"prettier": "2.5.0",
"eslint-plugin-prettier": "^4.0.0",
"prettier": "^2.5.1",
"@excalidraw/eslint-config": "1.0.0",
"@excalidraw/prettier-config": "1.0.2"
},

View File

@@ -19,6 +19,8 @@ import { tex2dataURL } from "./LaTeX";
import ExcalidrawPlugin from "./main";
import {
errorlog,
getDataURL,
getFontDataURL,
getImageSize,
getLinkParts,
LinkParts,
@@ -397,25 +399,9 @@ const convertMarkdownToSVG = async (
fontDef = "";
break;
default:
const f = plugin.app.metadataCache.getFirstLinkpathDest(
fontName,
file.path,
);
if (f) {
const ab = await plugin.app.vault.readBinary(f);
const mimeType = f.extension.startsWith("woff")
? "application/font-woff"
: "font/truetype";
fontName = f.basename;
fontDef = ` @font-face {font-family: "${fontName}";src: url("${await getDataURL(
ab,
mimeType,
)}") format("${f.extension === "ttf" ? "truetype" : f.extension}");}`;
const split = fontDef.split(";base64,", 2);
fontDef = `${split[0]};charset=utf-8;base64,${split[1]}`;
} else {
fontDef = "";
}
const font = await getFontDataURL(plugin.app, fontName, file.path);
fontDef = font.fontDef;
fontName = font.fontName;
}
const fontColor = fileCache?.frontmatter
@@ -552,21 +538,6 @@ const convertMarkdownToSVG = async (
return svgToBase64(finalSVG) as DataURL;
};
const getDataURL = async (
file: ArrayBuffer,
mimeType: string,
): Promise<DataURL> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const dataURL = reader.result as DataURL;
resolve(dataURL);
};
reader.onerror = (error) => reject(error);
reader.readAsDataURL(new Blob([new Uint8Array(file)], { type: mimeType }));
});
};
const generateIdFromFile = async (file: ArrayBuffer): Promise<FileId> => {
let id: FileId;
try {

View File

@@ -6,7 +6,7 @@ import {
ExcalidrawElement,
ExcalidrawBindableElement,
} from "@zsviczian/excalidraw/types/element/types";
import { Component, MarkdownRenderer, normalizePath, TFile } from "obsidian";
import { normalizePath, TFile, WorkspaceLeaf } from "obsidian";
import ExcalidrawView, { ExportSettings, TextMode } from "./ExcalidrawView";
import { ExcalidrawData } from "./ExcalidrawData";
import {
@@ -14,11 +14,13 @@ import {
nanoid,
VIEW_TYPE_EXCALIDRAW,
MAX_IMAGE_SIZE,
PLUGIN_ID,
} from "./constants";
import {
//debug,
embedFontsInSVG,
errorlog,
getNewOrAdjacentLeaf,
getPNG,
getSVG,
isObsidianThemeDark,
@@ -39,12 +41,6 @@ declare type ConnectionPoint = "top" | "bottom" | "left" | "right" | null;
const GAP = 4;
export interface ExcalidrawAutomate {
renderMarkdown(
markdown: string,
el: HTMLElement,
sourcePath: string,
component: Component,
): Promise<void>;
plugin: ExcalidrawPlugin;
elementsDict: {}; //contains the ExcalidrawElements currently edited in Automate indexed by el.id
imagesDict: {}; //the images files including DataURL, indexed by fileId
@@ -58,7 +54,7 @@ export interface ExcalidrawAutomate {
roughness: number;
opacity: number;
strokeSharpness: StrokeSharpness; //type StrokeSharpness = "round" | "sharp"
fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia
fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont
fontSize: number;
textAlign: string; //"left"|"right"|"center"
verticalAlign: string; //"top"|"bottom"|"middle" :for future use, has no effect currently
@@ -175,8 +171,13 @@ export interface ExcalidrawAutomate {
},
): boolean;
addElementsToView( //Adds elements from elementsDict to the current view
repositionToCursor: boolean,
save: boolean,
repositionToCursor?: boolean, //default is false
save?: boolean, //default is true
//newElementsOnTop controls whether elements created with ExcalidrawAutomate
//are added at the bottom of the stack or the top of the stack of elements already in the view
//Note that elements copied to the view with copyViewElementsToEAforEditing retain their
//position in the stack of elements in the view even if modified using EA
newElementsOnTop?: boolean, //default is false, i.e. the new elements get to the bottom of the stack
): Promise<boolean>;
onDropHook(data: {
//if set Excalidraw will call this function onDrop events
@@ -217,6 +218,17 @@ export interface ExcalidrawAutomate {
b: readonly [number, number],
gap?: number, //if given, element is inflated by this value
): Point[];
//See OCR plugin for example on how to use scriptSettings
activeScript: string; //Set automatically by the ScriptEngine
getScriptSettings(): {}; //Returns script settings. Saves settings in plugin settings, under the activeScript key
setScriptSettings(settings: any): Promise<void>; //sets script settings.
openFileInNewOrAdjacentLeaf(file: TFile): WorkspaceLeaf; //Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings
measureText(text: string): { width: number; height: number }; //measure text size based on current style settings
//verifyMinimumPluginVersion returns true if plugin version is >= than required
//recommended use:
//if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.20")) {new Notice("message");return;}
verifyMinimumPluginVersion(requiredVersion: string): boolean;
}
declare let window: any;
@@ -225,19 +237,6 @@ export async function initExcalidrawAutomate(
plugin: ExcalidrawPlugin,
): Promise<ExcalidrawAutomate> {
window.ExcalidrawAutomate = {
renderMarkdown(
markdown: string,
el: HTMLElement,
sourcePath: string,
component: Component,
): Promise<void> {
return MarkdownRenderer.renderMarkdown(
markdown,
el,
sourcePath,
component,
);
},
plugin,
elementsDict: {},
imagesDict: {},
@@ -298,14 +297,17 @@ export async function initExcalidrawAutomate(
setFontFamily(val: number) {
switch (val) {
case 1:
this.style.fontFamily = 1;
return getFontFamily(1);
this.style.fontFamily = 4;
return getFontFamily(4);
case 2:
this.style.fontFamily = 2;
return getFontFamily(2);
default:
case 3:
this.style.strokeSharpness = 3;
return getFontFamily(3);
default:
this.style.strokeSharpness = 1;
return getFontFamily(1);
}
},
setTheme(val: number) {
@@ -988,6 +990,7 @@ export async function initExcalidrawAutomate(
},
reset() {
this.clear();
this.activeScript = null;
this.style.strokeColor = "#000000";
this.style.backgroundColor = "transparent";
this.style.angle = 0;
@@ -1189,6 +1192,7 @@ export async function initExcalidrawAutomate(
async addElementsToView(
repositionToCursor: boolean = false,
save: boolean = true,
newElementsOnTop: boolean = false,
): Promise<boolean> {
if (!this.targetView || !this.targetView?._loaded) {
errorMessage("targetView not set", "addElementsToView()");
@@ -1200,6 +1204,7 @@ export async function initExcalidrawAutomate(
repositionToCursor,
save,
this.imagesDict,
newElementsOnTop,
);
},
onDropHook: null,
@@ -1256,6 +1261,43 @@ export async function initExcalidrawAutomate(
): Point[] {
return intersectElementWithLine(element, a, b, gap);
},
activeScript: null,
getScriptSettings(): {} {
if (!this.activeScript) {
return null;
}
return this.plugin.settings.scriptEngineSettings[this.activeScript] ?? {};
},
async setScriptSettings(settings: any): Promise<void> {
if (!this.activeScript) {
return null;
}
this.plugin.settings.scriptEngineSettings[this.activeScript] = settings;
await this.plugin.saveSettings();
},
openFileInNewOrAdjacentLeaf(file: TFile): WorkspaceLeaf {
if (!file || !(file instanceof TFile)) {
return null;
}
if (!this.targetView) {
return null;
}
const leaf = getNewOrAdjacentLeaf(this.plugin, this.targetView.leaf);
leaf.openFile(file);
return leaf;
},
measureText(text: string): { width: number; height: number } {
const size = measureText(
text,
this.style.fontSize,
this.style.fontFamily,
);
return { width: size.w, height: size.h };
},
verifyMinimumPluginVersion(requiredVersion: string): boolean {
const manifest = this.plugin.app.plugins.manifests[PLUGIN_ID];
return manifest.version >= requiredVersion;
},
};
await initFonts();
return window.ExcalidrawAutomate;
@@ -1329,6 +1371,8 @@ function getFontFamily(id: number) {
return "Helvetica, Segoe UI Emoji";
case 3:
return "Cascadia, Segoe UI Emoji";
case 4:
return "LocalFont";
}
}
@@ -1542,7 +1586,7 @@ export async function createSVG(
if (template?.hasSVGwithBitmap) {
svg.setAttribute("hasbitmap", "true");
}
return embedFont ? embedFontsInSVG(svg) : svg;
return embedFont ? embedFontsInSVG(svg, plugin) : svg;
}
function estimateLineBound(points: any): [number, number, number, number] {

View File

@@ -382,6 +382,11 @@ 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,

View File

@@ -46,6 +46,7 @@ import {
} from "./ExcalidrawData";
import {
checkAndCreateFolder,
checkExcalidrawVersion,
//debug,
download,
embedFontsInSVG,
@@ -151,9 +152,9 @@ export default class ExcalidrawView extends TextFileView {
private preventReload: boolean = true;
public compatibilityMode: boolean = false;
//store key state for view mode link resolution
private ctrlKeyDown = false;
/*private ctrlKeyDown = false;
private shiftKeyDown = false;
private altKeyDown = false;
private altKeyDown = false;*/
//https://stackoverflow.com/questions/27132796/is-there-any-javascript-event-fired-when-the-on-screen-keyboard-on-mobile-safari
private isEditingText: boolean = false;
@@ -204,7 +205,9 @@ export default class ExcalidrawView extends TextFileView {
return;
}
const serializer = new XMLSerializer();
const svgString = serializer.serializeToString(embedFontsInSVG(svg));
const svgString = serializer.serializeToString(
embedFontsInSVG(svg, this.plugin),
);
if (file && file instanceof TFile) {
await this.app.vault.modify(file, svgString);
} else {
@@ -344,7 +347,7 @@ export default class ExcalidrawView extends TextFileView {
return;
}
this.contentEl.requestFullscreen(); //{navigationUI: "hide"});
this.excalidrawWrapperRef.current.focus();
this.excalidrawWrapperRef.current.firstElementChild?.focus();
this.contentEl.setAttribute("style", "padding:0px;margin:0px;");
this.fullscreenModalObserver = new MutationObserver((m) => {
@@ -723,6 +726,7 @@ export default class ExcalidrawView extends TextFileView {
private isLoaded: boolean = false;
async setViewData(data: string, clear: boolean = false) {
checkExcalidrawVersion(this.app);
this.isLoaded = false;
if (clear) {
this.clear();
@@ -821,6 +825,10 @@ export default class ExcalidrawView extends TextFileView {
? om.zenModeEnabled
: this.excalidrawAPI.getAppState().zenModeEnabled;
//debug({where:"ExcalidrawView.loadDrawing",file:this.file.name,dataTheme:excalidrawData.appState.theme,before:"updateScene"})
this.excalidrawAPI.setLocalFont(
this.plugin.settings.experimentalEnableFourthFont,
);
this.excalidrawAPI.updateScene({
elements: excalidrawData.elements,
appState: {
@@ -835,7 +843,8 @@ export default class ExcalidrawView extends TextFileView {
this.app.workspace.activeLeaf === this.leaf &&
this.excalidrawWrapperRef
) {
this.excalidrawWrapperRef.current.focus();
//.firstElmentChild solves this issue: https://github.com/zsviczian/obsidian-excalidraw-plugin/pull/346
this.excalidrawWrapperRef.current?.firstElementChild?.focus();
}
//debug({where:"ExcalidrawView.loadDrawing",file:this.file.name,before:"this.loadSceneFiles"});
this.loadSceneFiles();
@@ -1004,7 +1013,7 @@ export default class ExcalidrawView extends TextFileView {
if (!svg) {
return null;
}
svg = embedFontsInSVG(svg);
svg = embedFontsInSVG(svg, this.plugin);
download(
null,
svgToBase64(svg.outerHTML),
@@ -1067,6 +1076,10 @@ export default class ExcalidrawView extends TextFileView {
this.excalidrawAPI = api;
//console.log({where:"ExcalidrawView.React.ReadyPromise"});
//debug({where:"ExcalidrawView.React.useEffect",file:this.file.name,before:"this.loadSceneFiles"});
this.excalidrawAPI.setLocalFont(
this.plugin.settings.experimentalEnableFourthFont,
);
this.loadSceneFiles();
this.updateContainerSize(null, true);
});
@@ -1233,6 +1246,7 @@ export default class ExcalidrawView extends TextFileView {
repositionToCursor: boolean = false,
save: boolean = false,
images: any,
newElementsOnTop: boolean = false,
): Promise<boolean> => {
if (!excalidrawRef?.current) {
return false;
@@ -1280,10 +1294,9 @@ export default class ExcalidrawView extends TextFileView {
const st: AppState = this.excalidrawAPI.getAppState();
//debug({where:"ExcalidrawView.addElements",file:this.file.name,dataTheme:this.excalidrawData.scene.appState.theme,before:"updateScene",state:st})
const elements = el.concat(
newElements.filter((e) => !removeList.includes(e.id)),
);
const elements = newElementsOnTop
? el.concat(newElements.filter((e) => !removeList.includes(e.id)))
: newElements.filter((e) => !removeList.includes(e.id)).concat(el);
this.excalidrawAPI.updateScene({
elements,
appState: st,
@@ -1324,7 +1337,7 @@ export default class ExcalidrawView extends TextFileView {
this.excalidrawAPI.addFiles(files);
}
if (save) {
this.save(false);
await this.save(false); //preventReload=false will ensure that markdown links are paresed and displayed correctly
} else {
this.dirty = this.file?.path;
}
@@ -1513,8 +1526,8 @@ export default class ExcalidrawView extends TextFileView {
const event = new MouseEvent("click", {
ctrlKey: true,
metaKey: true,
shiftKey: this.shiftKeyDown,
altKey: this.altKeyDown,
shiftKey: this.plugin.shiftKeyDown,
altKey: this.plugin.altKeyDown,
});
this.handleLinkClick(this, event);
selectedTextElement = null;
@@ -1524,8 +1537,8 @@ export default class ExcalidrawView extends TextFileView {
const event = new MouseEvent("click", {
ctrlKey: true,
metaKey: true,
shiftKey: this.shiftKeyDown,
altKey: this.altKeyDown,
shiftKey: this.plugin.shiftKeyDown,
altKey: this.plugin.altKeyDown,
});
this.handleLinkClick(this, event);
selectedImageElement = null;
@@ -1534,6 +1547,70 @@ export default class ExcalidrawView extends TextFileView {
let mouseEvent: any = null;
const showHoverPreview = () => {
let linktext = "";
const selectedElement = getTextElementAtPointer(currentPosition);
if (!selectedElement || !selectedElement.text) {
const selectedImgElement = getImageElementAtPointer(currentPosition);
if (!selectedImgElement || !selectedImgElement.fileId) {
return;
}
if (!this.excalidrawData.hasFile(selectedImgElement.fileId)) {
return;
}
const ef = this.excalidrawData.getFile(selectedImgElement.fileId);
const ref = ef.linkParts.ref
? `#${ef.linkParts.isBlockRef ? "^" : ""}${ef.linkParts.ref}`
: "";
linktext =
this.excalidrawData.getFile(selectedImgElement.fileId).file.path +
ref;
} else {
const text: string =
this.textMode === TextMode.parsed
? this.excalidrawData.getRawText(selectedElement.id)
: selectedElement.text;
if (!text) {
return;
}
if (text.match(REG_LINKINDEX_HYPERLINK)) {
return;
}
const parts = REGEX_LINK.getRes(text).next();
if (!parts.value) {
return;
}
linktext = REGEX_LINK.getLink(parts); //parts.value[2] ? parts.value[2]:parts.value[6];
if (linktext.match(REG_LINKINDEX_HYPERLINK)) {
return;
}
}
this.plugin.hover.linkText = linktext;
this.plugin.hover.sourcePath = this.file.path;
hoverPreviewTarget = this.contentEl; //e.target;
this.app.workspace.trigger("hover-link", {
event: mouseEvent,
source: VIEW_TYPE_EXCALIDRAW,
hoverParent: hoverPreviewTarget,
targetEl: hoverPreviewTarget,
linktext: this.plugin.hover.linkText,
sourcePath: this.plugin.hover.sourcePath,
});
hoverPoint = currentPosition;
if (this.isFullscreen()) {
const self = this;
setTimeout(() => {
const popover = document.body.querySelector("div.popover");
if (popover) {
self.contentEl.append(popover);
}
}, 100);
}
};
const excalidrawDiv = React.createElement(
"div",
{
@@ -1550,83 +1627,20 @@ export default class ExcalidrawView extends TextFileView {
this.exitFullscreen();
}
/*
this.ctrlKeyDown = e[CTRL_OR_CMD]; //.ctrlKey||e.metaKey;
this.shiftKeyDown = e.shiftKey;
this.altKeyDown = e.altKey;
this.altKeyDown = e.altKey;*/
if (e[CTRL_OR_CMD] && !e.shiftKey && !e.altKey) {
//.ctrlKey||e.metaKey) && !e.shiftKey && !e.altKey) {
let linktext = "";
const selectedElement = getTextElementAtPointer(currentPosition);
if (!selectedElement || !selectedElement.text) {
const selectedImgElement =
getImageElementAtPointer(currentPosition);
if (!selectedImgElement || !selectedImgElement.fileId) {
return;
}
if (!this.excalidrawData.hasFile(selectedImgElement.fileId)) {
return;
}
const ef = this.excalidrawData.getFile(
selectedImgElement.fileId,
);
const ref = ef.linkParts.ref
? `#${ef.linkParts.isBlockRef ? "^" : ""}${ef.linkParts.ref}`
: "";
linktext =
this.excalidrawData.getFile(selectedImgElement.fileId).file
.path + ref;
} else {
const text: string =
this.textMode === TextMode.parsed
? this.excalidrawData.getRawText(selectedElement.id)
: selectedElement.text;
if (!text) {
return;
}
if (text.match(REG_LINKINDEX_HYPERLINK)) {
return;
}
const parts = REGEX_LINK.getRes(text).next();
if (!parts.value) {
return;
}
linktext = REGEX_LINK.getLink(parts); //parts.value[2] ? parts.value[2]:parts.value[6];
if (linktext.match(REG_LINKINDEX_HYPERLINK)) {
return;
}
}
this.plugin.hover.linkText = linktext;
this.plugin.hover.sourcePath = this.file.path;
hoverPreviewTarget = this.contentEl; //e.target;
this.app.workspace.trigger("hover-link", {
event: mouseEvent,
source: VIEW_TYPE_EXCALIDRAW,
hoverParent: hoverPreviewTarget,
targetEl: hoverPreviewTarget,
linktext: this.plugin.hover.linkText,
sourcePath: this.plugin.hover.sourcePath,
});
hoverPoint = currentPosition;
if (this.isFullscreen()) {
const self = this;
setTimeout(() => {
const popover = document.body.querySelector("div.popover");
if (popover) {
self.contentEl.append(popover);
}
}, 100);
}
showHoverPreview();
}
},
onKeyUp: (e: any) => {
/* onKeyUp: (e: any) => {
this.ctrlKeyDown = e[CTRL_OR_CMD]; //.ctrlKey||e.metaKey;
this.shiftKeyDown = e.shiftKey;
this.altKeyDown = e.altKey;
},
},*/
onClick: (e: MouseEvent): any => {
if (!e[CTRL_OR_CMD]) {
return;
@@ -1695,7 +1709,7 @@ export default class ExcalidrawView extends TextFileView {
blockOnMouseButtonDown = true;
//ctrl click
if (this.ctrlKeyDown) {
if (this.plugin.ctrlKeyDown) {
handleLinkClick();
return;
}
@@ -1711,6 +1725,9 @@ export default class ExcalidrawView extends TextFileView {
if (p.button === "up") {
blockOnMouseButtonDown = false;
}
if (this.plugin.ctrlKeyDown) {
showHoverPreview();
}
},
onChange: (et: ExcalidrawElement[], st: AppState) => {
viewModeEnabled = st.viewModeEnabled;
@@ -2001,7 +2018,7 @@ export default class ExcalidrawView extends TextFileView {
return React.createElement(React.Fragment, null, excalidrawDiv);
});
ReactDOM.render(reactElement, this.contentEl, () => {
this.excalidrawWrapperRef.current.focus();
this.excalidrawWrapperRef.current.firstElementChild?.focus();
this.addFullscreenchangeEvent();
});
}

View File

@@ -0,0 +1,490 @@
import {
MarkdownPostProcessorContext,
MetadataCache,
TFile,
Vault,
} from "obsidian";
import { CTRL_OR_CMD, RERENDER_EVENT } from "./constants";
import { EmbeddedFilesLoader } from "./EmbeddedFileLoader";
import { createPNG, createSVG } from "./ExcalidrawAutomate";
import { ExportSettings } from "./ExcalidrawView";
import ExcalidrawPlugin from "./main";
import {
embedFontsInSVG,
getIMGFilename,
isObsidianThemeDark,
splitFolderAndFilename,
svgToBase64,
} from "./Utils";
interface imgElementAttributes {
file?: TFile;
fname: string; //Excalidraw filename
fwidth: string; //Display width of image
fheight: string; //Display height of image
style: string; //css style to apply to IMG element
}
let plugin: ExcalidrawPlugin;
let vault: Vault;
let metadataCache: MetadataCache;
export const initializeMarkdownPostProcessor = (p: ExcalidrawPlugin) => {
plugin = p;
vault = p.app.vault;
metadataCache = p.app.metadataCache;
};
/**
* Generates an img element with the drawing encoded as a base64 SVG or a PNG (depending on settings)
* @param parts {imgElementAttributes} - display properties of the image
* @returns {Promise<HTMLElement>} - the IMG HTML element containing the image
*/
const getIMG = async (
imgAttributes: imgElementAttributes,
): Promise<HTMLElement> => {
let file = imgAttributes.file;
if (!imgAttributes.file) {
const f = vault.getAbstractFileByPath(imgAttributes.fname);
if (!(f && f instanceof TFile)) {
return null;
}
file = f;
}
const exportSettings: ExportSettings = {
withBackground: plugin.settings.exportWithBackground,
withTheme: plugin.settings.exportWithTheme,
};
const img = createEl("img");
let style = `max-width:${imgAttributes.fwidth}px !important; width:100%;`;
if (imgAttributes.fheight) {
style += `height:${imgAttributes.fheight}px;`;
}
img.setAttribute("style", style);
img.addClass(imgAttributes.style);
const theme = plugin.settings.previewMatchObsidianTheme
? isObsidianThemeDark()
? "dark"
: "light"
: !plugin.settings.exportWithTheme
? "light"
: undefined;
if (theme) {
exportSettings.withTheme = true;
}
const loader = new EmbeddedFilesLoader(
plugin,
theme ? theme === "dark" : undefined,
);
if (!plugin.settings.displaySVGInPreview) {
const width = parseInt(imgAttributes.fwidth);
let scale = 1;
if (width >= 600) {
scale = 2;
}
if (width >= 1200) {
scale = 3;
}
if (width >= 1800) {
scale = 4;
}
if (width >= 2400) {
scale = 5;
}
const png = await createPNG(
file.path,
scale,
exportSettings,
loader,
theme,
null,
null,
[],
plugin,
);
if (!png) {
return null;
}
img.src = URL.createObjectURL(png);
return img;
}
const svgSnapshot = (
await createSVG(
file.path,
true,
exportSettings,
loader,
theme,
null,
null,
[],
plugin,
)
).outerHTML;
let svg: SVGSVGElement = null;
const el = document.createElement("div");
el.innerHTML = svgSnapshot;
const firstChild = el.firstChild;
if (firstChild instanceof SVGSVGElement) {
svg = firstChild;
}
if (!svg) {
return null;
}
svg = embedFontsInSVG(svg, plugin);
svg.removeAttribute("width");
svg.removeAttribute("height");
img.setAttribute("src", svgToBase64(svg.outerHTML));
return img;
};
const createImageDiv = async (
attr: imgElementAttributes,
): Promise<HTMLDivElement> => {
const img = await getIMG(attr);
return createDiv(attr.style, (el) => {
el.append(img);
el.setAttribute("src", attr.file.path);
if (attr.fwidth) {
el.setAttribute("w", attr.fwidth);
}
if (attr.fheight) {
el.setAttribute("h", attr.fheight);
}
el.onClickEvent((ev) => {
if (
ev.target instanceof Element &&
ev.target.tagName.toLowerCase() != "img"
) {
return;
}
const src = el.getAttribute("src");
if (src) {
plugin.openDrawing(
vault.getAbstractFileByPath(src) as TFile,
ev[CTRL_OR_CMD],
);
} //.ctrlKey||ev.metaKey);
});
el.addEventListener(RERENDER_EVENT, async (e) => {
e.stopPropagation();
el.empty();
const img = await getIMG({
fname: el.getAttribute("src"),
fwidth: el.getAttribute("w"),
fheight: el.getAttribute("h"),
style: el.getAttribute("class"),
});
el.append(img);
});
});
};
const processInternalEmbeds = async (
embeddedItems: NodeListOf<Element> | [HTMLElement],
ctx: MarkdownPostProcessorContext,
) => {
//if not, then we are processing a non-excalidraw file in reading mode
//in that cases embedded files will be displayed in an .internal-embed container
const attr: imgElementAttributes = {
fname: "",
fheight: "",
fwidth: "",
style: "",
};
let alt: string;
let parts;
let file: TFile;
//Iterating through all the containers to check which one is an excalidraw drawing
//This is a for loop instead of embeddedItems.forEach() because createImageDiv at the end
//is awaited, otherwise excalidraw images would not display in the Kanban plugin
for (const maybeDrawing of embeddedItems) {
//check to see if the file in the src attribute exists
attr.fname = maybeDrawing.getAttribute("src");
file = metadataCache.getFirstLinkpathDest(
attr.fname?.split("#")[0],
ctx.sourcePath,
);
//if the embeddedFile exits and it is an Excalidraw file
//then lets replace the .internal-embed with the generated PNG or SVG image
if (file && file instanceof TFile && plugin.isExcalidrawFile(file)) {
attr.fwidth = maybeDrawing.getAttribute("width")
? maybeDrawing.getAttribute("width")
: plugin.settings.width;
attr.fheight = maybeDrawing.getAttribute("height");
alt = maybeDrawing.getAttribute("alt");
if (alt == attr.fname) {
alt = "";
} //when the filename starts with numbers followed by a space Obsidian recognizes the filename as alt-text
attr.style = "excalidraw-svg";
if (alt) {
//for some reason Obsidian renders ![]() in a DIV and ![[]] in a SPAN
//also the alt-text of the DIV does not include the alt-text of the image
//thus need to add an additional "|" character when its a SPAN
if (maybeDrawing.tagName.toLowerCase() == "span") {
alt = `|${alt}`;
}
//1:width, 2:height, 3:style 1 2 3
parts = alt.match(/[^\|]*\|?(\d*%?)x?(\d*%?)\|?(.*)/);
attr.fwidth = parts[1] ? parts[1] : plugin.settings.width;
attr.fheight = parts[2];
if (parts[3] != attr.fname) {
attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`;
}
}
attr.fname = file?.path;
attr.file = file;
const div = await createImageDiv(attr);
maybeDrawing.parentElement.replaceChild(div, maybeDrawing);
}
}
};
const tmpObsidianWYSIWYG = async (
el: HTMLElement,
ctx: MarkdownPostProcessorContext,
) => {
if (!ctx.frontmatter) {
return;
}
if (!ctx.frontmatter.hasOwnProperty("excalidraw-plugin")) {
return;
}
//@ts-ignore
if (ctx.remainingNestLevel < 4) {
return;
}
if (!el.querySelector(".frontmatter")) {
el.style.display = "none";
return;
}
const attr: imgElementAttributes = {
fname: ctx.sourcePath,
fheight: "",
fwidth: plugin.settings.width,
style: "excalidraw-svg",
};
attr.file = metadataCache.getFirstLinkpathDest(ctx.sourcePath, "");
el.empty();
if (!plugin.settings.experimentalLivePreview) {
el.appendChild(await createImageDiv(attr));
return;
}
const div = createDiv();
el.appendChild(div);
//The timeout gives time for obsidian to attach el to the displayed document
//Once the element is attached, I can traverse up the dom tree to find .internal-embed
//If internal embed is not found, it means the that the excalidraw.md file
//is being rendered in "reading" mode. In that case, the image with the default width
//specified in setting should be displayed
//if .internal-embed is found, then contents is replaced with the image using the
//alt, width, and height attributes of .internal-embed to size and style the image
setTimeout(async () => {
let internalEmbedDiv: HTMLElement = div;
while (
!internalEmbedDiv.hasClass("internal-embed") &&
internalEmbedDiv.parentElement
) {
internalEmbedDiv = internalEmbedDiv.parentElement;
}
if (!internalEmbedDiv.hasClass("internal-embed")) {
el.empty();
el.appendChild(await createImageDiv(attr));
return;
}
internalEmbedDiv.empty();
const basename = splitFolderAndFilename(attr.fname).basename;
const setAttr = () => {
const hasWidth = internalEmbedDiv.getAttribute("width") !== "";
const hasHeight = internalEmbedDiv.getAttribute("height") !== "";
if (hasWidth) {
attr.fwidth = internalEmbedDiv.getAttribute("width");
}
if (hasHeight) {
attr.fheight = internalEmbedDiv.getAttribute("height");
}
const alt = internalEmbedDiv.getAttribute("alt");
const hasAttr =
alt &&
alt !== "" &&
alt !== basename &&
alt !== internalEmbedDiv.getAttribute("src");
if (hasAttr) {
//1:width, 2:height, 3:style 1 2 3
const parts = alt.match(/(\d*%?)x?(\d*%?)\|?(.*)/);
attr.fwidth = parts[1] ? parts[1] : plugin.settings.width;
attr.fheight = parts[2];
if (parts[3] != attr.fname) {
attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`;
}
}
if (!hasWidth && !hasHeight && !hasAttr) {
attr.fheight = "";
attr.fwidth = plugin.settings.width;
attr.style = "excalidraw-svg";
}
};
const createImgElement = async () => {
setAttr();
const imgDiv = await createImageDiv(attr);
internalEmbedDiv.appendChild(imgDiv);
};
await createImgElement();
//timer to avoid the image flickering when the user is typing
let timer: NodeJS.Timeout = null;
const observer = new MutationObserver((m) => {
if (!["alt", "width", "height"].contains(m[0]?.attributeName)) {
return;
}
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
timer = null;
setAttr();
internalEmbedDiv.empty();
createImgElement();
}, 500);
});
observer.observe(internalEmbedDiv, {
attributes: true, //configure it to listen to attribute changes
});
}, 300);
};
/**
*
* @param el
* @param ctx
*/
export const markdownPostProcessor = async (
el: HTMLElement,
ctx: MarkdownPostProcessorContext,
) => {
//check to see if we are rendering in editing mode of live preview
//if yes, then there should be no .internal-embed containers
const embeddedItems = el.querySelectorAll(".internal-embed");
if (embeddedItems.length === 0) {
tmpObsidianWYSIWYG(el, ctx);
return;
}
//If the file being processed is an excalidraw file,
//then I want to hide all embedded items as these will be
//transcluded text element or some other transcluded content inside the Excalidraw file
//in reading mode these elements should be hidden
if (ctx.frontmatter?.hasOwnProperty("excalidraw-plugin")) {
el.style.display = "none";
return;
}
await processInternalEmbeds(embeddedItems, ctx);
};
/**
* internal-link quick preview
* @param e
* @returns
*/
export const hoverEvent = (e: any) => {
if (!e.linktext) {
plugin.hover.linkText = null;
return;
}
plugin.hover.linkText = e.linktext;
plugin.hover.sourcePath = e.sourcePath;
};
//monitoring for div.popover.hover-popover.file-embed.is-loaded to be added to the DOM tree
export const observer = new MutationObserver(async (m) => {
if (m.length == 0) {
return;
}
if (!plugin.hover.linkText) {
return;
}
const file = metadataCache.getFirstLinkpathDest(
plugin.hover.linkText,
plugin.hover.sourcePath ? plugin.hover.sourcePath : "",
);
if (!file) {
return;
}
if (!(file instanceof TFile)) {
return;
}
if (file.extension !== "excalidraw") {
return;
}
const svgFileName = getIMGFilename(file.path, "svg");
const svgFile = vault.getAbstractFileByPath(svgFileName);
if (svgFile && svgFile instanceof TFile) {
return;
} //If auto export SVG or PNG is enabled it will be inserted at the top of the excalidraw file. No need to manually insert hover preview
const pngFileName = getIMGFilename(file.path, "png");
const pngFile = vault.getAbstractFileByPath(pngFileName);
if (pngFile && pngFile instanceof TFile) {
return;
} //If auto export SVG or PNG is enabled it will be inserted at the top of the excalidraw file. No need to manually insert hover preview
if (!plugin.hover.linkText) {
return;
}
if (m.length != 1) {
return;
}
if (m[0].addedNodes.length != 1) {
return;
}
if (
//@ts-ignore
m[0].addedNodes[0].className != "popover hover-popover file-embed is-loaded"
) {
return;
}
const node = m[0].addedNodes[0];
node.empty();
//this div will be on top of original DIV. By stopping the propagation of the click
//I prevent the default Obsidian feature of openning the link in the native app
const img = await getIMG({
file,
fname: file.path,
fwidth: "300",
fheight: null,
style: "excalidraw-svg",
});
const div = createDiv("", async (el) => {
el.appendChild(img);
el.setAttribute("src", file.path);
el.onClickEvent((ev) => {
ev.stopImmediatePropagation();
const src = el.getAttribute("src");
if (src) {
plugin.openDrawing(
vault.getAbstractFileByPath(src) as TFile,
ev[CTRL_OR_CMD],
);
} //.ctrlKey||ev.metaKey);
});
});
node.appendChild(div);
});

View File

@@ -5,6 +5,7 @@ import {
TextComponent,
FuzzyMatch,
FuzzySuggestModal,
Instruction,
} from "obsidian";
export class Prompt extends Modal {
@@ -211,25 +212,43 @@ export class GenericInputPrompt extends Modal {
}
}
export class GenericSuggester extends FuzzySuggestModal<string> {
private resolvePromise: (value: string) => void;
export class GenericSuggester extends FuzzySuggestModal<any> {
private resolvePromise: (value: any) => void;
private rejectPromise: (reason?: any) => void;
public promise: Promise<string>;
public promise: Promise<any>;
private resolved: boolean;
public static Suggest(app: App, displayItems: string[], items: string[]) {
const newSuggester = new GenericSuggester(app, displayItems, items);
public static Suggest(
app: App,
displayItems: string[],
items: string[],
hint?: string,
instructions?: Instruction[],
) {
const newSuggester = new GenericSuggester(
app,
displayItems,
items,
hint,
instructions,
);
return newSuggester.promise;
}
public constructor(
app: App,
private displayItems: string[],
private items: string[],
private items: any[],
private hint?: string,
private instructions?: Instruction[],
) {
super(app);
this.promise = new Promise<string>((resolve, reject) => {
this.limit = 20;
this.setPlaceholder(this.hint ?? "");
if (instructions) {
this.setInstructions(this.instructions);
}
this.promise = new Promise<any>((resolve, reject) => {
this.resolvePromise = resolve;
this.rejectPromise = reject;
});
@@ -241,7 +260,7 @@ export class GenericSuggester extends FuzzySuggestModal<string> {
return this.displayItems[this.items.indexOf(item)];
}
getItems(): string[] {
getItems(): any[] {
return this.items;
}
@@ -250,16 +269,15 @@ export class GenericSuggester extends FuzzySuggestModal<string> {
super.selectSuggestion(value, evt);
}
onChooseItem(item: string): void {
onChooseItem(item: any): void {
this.resolved = true;
this.resolvePromise(item);
}
onClose() {
super.onClose();
if (!this.resolved) {
this.rejectPromise("no input given.");
this.rejectPromise(this.inputEl.value);
}
}
}

View File

@@ -1,8 +1,6 @@
import { App, MarkdownRenderer, Modal, Notice, request } from "obsidian";
import { Url } from "url";
import { t } from "./lang/helpers";
import { MarkdownRenderer, Modal, Notice, request } from "obsidian";
import ExcalidrawPlugin from "./main";
import { errorlog } from "./Utils";
import { errorlog, log } from "./Utils";
const URL =
"https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/index.md";
@@ -18,15 +16,28 @@ export class ScriptInstallPrompt extends Modal {
this.containerEl.classList.add("excalidraw-scriptengine-install");
try {
const source = await request({ url: URL });
if (!source) {
new Notice(
"Error opening the Excalidraw Script Store page. " +
"Please double check that you can access the website. " +
"I've logged the link in developer console (press CTRL+SHIFT+i)",
5000,
);
log(URL);
this.close();
return;
}
await MarkdownRenderer.renderMarkdown(
source,
this.contentEl,
"",
this.plugin,
);
this.contentEl.querySelectorAll("h1[data-heading],h2[data-heading],h3[data-heading]").forEach((el) => {
el.setAttribute("id", el.getAttribute("data-heading"));
});
this.contentEl
.querySelectorAll("h1[data-heading],h2[data-heading],h3[data-heading]")
.forEach((el) => {
el.setAttribute("id", el.getAttribute("data-heading"));
});
this.contentEl.querySelectorAll("a.internal-link").forEach((el) => {
el.removeAttribute("target");
});

View File

@@ -1,6 +1,5 @@
import { sub } from "@zsviczian/excalidraw/types/ga";
import { App, TAbstractFile, TFile } from "obsidian";
import { VIEW_TYPE_EXCALIDRAW } from "./constants";
import { App, Instruction, TAbstractFile, TFile } from "obsidian";
import { PLUGIN_ID, VIEW_TYPE_EXCALIDRAW } from "./constants";
import ExcalidrawView from "./ExcalidrawView";
import ExcalidrawPlugin from "./main";
import { GenericInputPrompt, GenericSuggester } from "./Prompt";
@@ -65,23 +64,29 @@ export class ScriptEngine {
if (this.scriptPath === this.plugin.settings.scriptFolderPath) {
return;
}
this.unloadScripts();
if (this.scriptPath) {
this.unloadScripts();
}
this.loadScripts();
}
loadScripts() {
const app = this.plugin.app;
this.scriptPath = this.plugin.settings.scriptFolderPath;
if (!app.vault.getAbstractFileByPath(this.scriptPath)) {
this.scriptPath = null;
return;
}
const scripts = app.vault
.getFiles()
.filter((f: TFile) => f.path.startsWith(this.scriptPath));
scripts.forEach((f) => this.loadScript(f));
}
getScriptName(f:TFile|string):string {
getScriptName(f: TFile | string): string {
let basename = "";
let path = "";
if(f instanceof TFile) {
if (f instanceof TFile) {
basename = f.basename;
path = f.path;
} else {
@@ -89,9 +94,7 @@ export class ScriptEngine {
path = f;
}
const subpath = path.split(
`${this.plugin.settings.scriptFolderPath}/`,
)[1];
const subpath = path.split(`${this.scriptPath}/`)[1];
const lastSlash = subpath.lastIndexOf("/");
if (lastSlash > -1) {
return subpath.substring(0, lastSlash + 1) + basename;
@@ -133,7 +136,7 @@ export class ScriptEngine {
unloadScript(basename: string) {
const app = this.plugin.app;
const commandId = `obsidian-excalidraw-plugin:${basename}`;
const commandId = `${PLUGIN_ID}:${basename}`;
// @ts-ignore
if (!app.commands.commands[commandId]) {
return;
@@ -153,16 +156,33 @@ export class ScriptEngine {
return;
}
this.plugin.ea.activeScript = this.getScriptName(f);
//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;
return await new AsyncFunction("ea", "utils", script)(this.plugin.ea, {
inputPrompt: (header: string, placeholder?: string, value?: string) =>
ScriptEngine.inputPrompt(this.plugin.app, header, placeholder, value),
suggester: (displayItems: string[], items: string[]) =>
ScriptEngine.suggester(this.plugin.app, displayItems, items),
});
const result = await new AsyncFunction("ea", "utils", script)(
this.plugin.ea,
{
inputPrompt: (header: string, placeholder?: string, value?: string) =>
ScriptEngine.inputPrompt(this.plugin.app, header, placeholder, value),
suggester: (
displayItems: string[],
items: any[],
hint?: string,
instructions?: Instruction[],
) =>
ScriptEngine.suggester(
this.plugin.app,
displayItems,
items,
hint,
instructions,
),
},
);
this.plugin.ea.activeScript = null;
return result;
}
public static async inputPrompt(
@@ -181,11 +201,19 @@ export class ScriptEngine {
public static async suggester(
app: App,
displayItems: string[],
items: string[],
items: any[],
hint?: string,
instructions?: Instruction[],
) {
try {
return await GenericSuggester.Suggest(app, displayItems, items);
} catch {
return await GenericSuggester.Suggest(
app,
displayItems,
items,
hint,
instructions,
);
} catch (e) {
return undefined;
}
}

View File

@@ -2,14 +2,21 @@ import { exportToSvg, exportToBlob } from "@zsviczian/excalidraw";
import {
App,
normalizePath,
Notice,
request,
TAbstractFile,
TFolder,
Vault,
WorkspaceLeaf,
} from "obsidian";
import { Random } from "roughjs/bin/math";
import { Zoom } from "@zsviczian/excalidraw/types/types";
import { CASCADIA_FONT, REG_BLOCK_REF_CLEAN, VIRGIL_FONT } from "./constants";
import { DataURL, Zoom } from "@zsviczian/excalidraw/types/types";
import {
CASCADIA_FONT,
REG_BLOCK_REF_CLEAN,
VIRGIL_FONT,
PLUGIN_ID,
} from "./constants";
import ExcalidrawPlugin from "./main";
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/element/types";
import { ExportSettings } from "./ExcalidrawView";
@@ -26,6 +33,45 @@ declare module "obsidian" {
}
}
let versionUpdateChecked = false;
export const checkExcalidrawVersion = async (app: App) => {
if (versionUpdateChecked) {
return;
}
versionUpdateChecked = true;
//@ts-ignore
const manifest = app.plugins.manifests[PLUGIN_ID];
try {
const gitAPIrequest = async () => {
return JSON.parse(
await request({
url: `https://api.github.com/repos/zsviczian/obsidian-excalidraw-plugin/releases?per_page=5&page=1`,
}),
);
};
const latestVersion = (await gitAPIrequest())
.map((el: any) => {
return {
version: el.tag_name,
published: new Date(el.published_at),
};
})
.filter((el: any) => el.version.match(/^\d+\.\d+\.\d+$/))
.sort((el1: any, el2: any) => el2.published - el1.published)[0].version;
if (latestVersion > manifest.version) {
new Notice(
`A newer version of Excalidraw is available in Community Plugins.\n\nYou are using ${manifest.version}.\nThe latest is ${latestVersion}`,
);
}
} catch (e) {
errorlog({ where: "Utils/checkExcalidrawVersion", error: e });
}
setTimeout(() => (versionUpdateChecked = false), 28800000); //reset after 8 hours
};
/**
* Splits a full path including a folderpath and a filename into separate folderpath and filename components
* @param filepath
@@ -265,6 +311,47 @@ export const getNewOrAdjacentLeaf = (
return plugin.app.workspace.createLeafBySplit(leaf);
};
export const getDataURL = async (
file: ArrayBuffer,
mimeType: string,
): Promise<DataURL> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const dataURL = reader.result as DataURL;
resolve(dataURL);
};
reader.onerror = (error) => reject(error);
reader.readAsDataURL(new Blob([new Uint8Array(file)], { type: mimeType }));
});
};
export const getFontDataURL = async (
app: App,
fontFileName: string,
sourcePath: string,
name?: string,
): Promise<{ fontDef: string; fontName: string; dataURL: string }> => {
let fontDef: string = "";
let fontName = "";
let dataURL = "";
const f = app.metadataCache.getFirstLinkpathDest(fontFileName, sourcePath);
if (f) {
const ab = await app.vault.readBinary(f);
const mimeType = f.extension.startsWith("woff")
? "application/font-woff"
: "font/truetype";
fontName = name ?? f.basename;
dataURL = await getDataURL(ab, mimeType);
fontDef = ` @font-face {font-family: "${fontName}";src: url("${dataURL}") format("${
f.extension === "ttf" ? "truetype" : f.extension
}");}`;
const split = fontDef.split(";base64,", 2);
fontDef = `${split[0]};charset=utf-8;base64,${split[1]}`;
}
return { fontDef, fontName, dataURL };
};
export const svgToBase64 = (svg: string): string => {
return `data:image/svg+xml;base64,${btoa(
unescape(encodeURIComponent(svg.replaceAll("&nbsp;", " "))),
@@ -308,7 +395,9 @@ export const getAttachmentsFolderAndFilePath = async (
await checkAndCreateFolder(app.vault, folder);
return {
folder,
filepath: normalizePath(`${folder}/${newFileName}`),
filepath: normalizePath(
folder === "" ? newFileName : `${folder}/${newFileName}`,
),
};
};
@@ -363,17 +452,22 @@ export const getPNG = async (
}
};
export const embedFontsInSVG = (svg: SVGSVGElement): SVGSVGElement => {
export const embedFontsInSVG = (
svg: SVGSVGElement,
plugin: ExcalidrawPlugin,
): SVGSVGElement => {
//replace font references with base64 fonts
const includesVirgil =
svg.querySelector("text[font-family^='Virgil']") != null;
const includesCascadia =
svg.querySelector("text[font-family^='Cascadia']") != null;
const includesLocalFont =
svg.querySelector("text[font-family^='LocalFont']") != null;
const defs = svg.querySelector("defs");
if (defs && (includesCascadia || includesVirgil)) {
if (defs && (includesCascadia || includesVirgil || includesLocalFont)) {
defs.innerHTML = `<style>${includesVirgil ? VIRGIL_FONT : ""}${
includesCascadia ? CASCADIA_FONT : ""
}</style>`;
}${includesLocalFont ? plugin.fourthFontDef : ""}</style>`;
}
return svg;
};

File diff suppressed because one or more lines are too long

View File

@@ -9,9 +9,12 @@ export default {
// main.ts
INSTALL_SCRIPT: "Install this script",
UPDATE_SCRIPT: "An update is available - Click to install",
CHECKING_SCRIPT: "Checking if a newer version is available - Click to reinstall now",
UNABLETOCHECK_SCRIPT: "Update check was unsuccessful - Click to reinstall now",
UPTODATE_SCRIPT: "Script is installed and up to date - Click to reinstall now",
CHECKING_SCRIPT:
"Checking if a newer version is available - Click to reinstall now",
UNABLETOCHECK_SCRIPT:
"Update check was unsuccessful - Click to reinstall now",
UPTODATE_SCRIPT:
"Script is installed and up to date - Click to reinstall now",
OPEN_AS_EXCALIDRAW: "Open as Excalidraw Drawing",
TOGGLE_MODE: "Toggle between Excalidraw and Markdown mode",
CONVERT_NOTE_TO_EXCALIDRAW: "Convert empty note to Excalidraw Drawing",
@@ -73,6 +76,12 @@ export default {
FOLDER_NAME: "Excalidraw folder",
FOLDER_DESC:
"Default location for new drawings. If empty, drawings will be created in the Vault root.",
FOLDER_EMBED_NAME:
"Use Excalidraw folder when embedding a drawing into the active document",
FOLDER_EMBED_DESC:
"Define which folder to place the newly inserted drawing into " +
"when using the command palette action: 'Create a new drawing and embed into active document'. " +
"ON: Use Excalidraw folder; OFF: use attachments folder defined in Obsidian settings",
TEMPLATE_NAME: "Excalidraw template file",
TEMPLATE_DESC:
"Full filepath to the Excalidraw template. " +
@@ -101,6 +110,11 @@ export default {
FILENAME_SAMPLE: "The current file format is: <b>",
FILENAME_PREFIX_NAME: "Filename prefix",
FILENAME_PREFIX_DESC: "The first part of the filename",
FILENAME_PREFIX_EMBED_NAME: "Filename prefix for embedded files",
FILENAME_PREFIX_EMBED_DESC:
"Name of the newly inserted drawing should start with the name of the active note " +
"when using the command palette action: 'Create a new drawing and embed into active document'. " +
"On: yes, Off: not",
FILENAME_DATE_NAME: "Filename date",
FILENAME_DATE_DESC: "The second part of the filename",
/*SVG_IN_MD_NAME: "SVG Snapshot to markdown file",
@@ -271,6 +285,22 @@ export default {
FILETAG_NAME: "Set the type indicator for excalidraw.md files",
FILETAG_DESC: "The text or emojii to display as type indicator.",
INSERT_EMOJI: "Insert an emoji",
LIVEPREVIEW_NAME: "Immersive image embedding in live preview editing mode",
LIVEPREVIEW_DESC:
"Turn this on to support image embedding styles such as ![[drawing|width|style]] in live preview editing mode. " +
"The setting will not effect the currently open documents. You need close the open documents and re-open them for the change " +
"to take effect.",
ENABLE_FOURTH_FONT_NAME: "Enable fourth font option",
ENABLE_FOURTH_FONT_DESC:
"By turning this on, you will see a fourth font button on the properties panel for text elements. " +
"Files that use this fourth font will (partly) lose their paltform independence. " +
"Depending on the cutom font set in settings, they will look differently when loaded in another vault, or at a later time. " +
"Also the 4th font will display as system default font on excalidraw.com, or other Excalidraw versions.",
FOURTH_FONT_NAME: "Forth font file",
FOURTH_FONT_DESC:
"Select a .ttf, .woff or .woff2 font file from your vault to use as the fourth font. " +
"If no file is selected excalidraw will use the Virgil font by default.",
SCRIPT_SETTINGS_HEAD: "Settings for installed Scripts",
//openDrawings.ts
SELECT_FILE: "Select a file then press enter.",

View File

@@ -1,161 +1,291 @@
// 简体中文
import {
FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS,
FRONTMATTER_KEY_CUSTOM_PREFIX,
FRONTMATTER_KEY_CUSTOM_URL_PREFIX,
} from "src/constants";
// 简体中文
export default {
// main.ts
INSTALL_SCRIPT: "安装此脚本",
UPDATE_SCRIPT: "发现可用更新 - 点击安装",
CHECKING_SCRIPT: "检查更新 - 点击重新安装",
UNABLETOCHECK_SCRIPT: "检查更新失败 - 点击重新安装",
UPTODATE_SCRIPT: "已安装最新脚本 - 点击重新安装",
OPEN_AS_EXCALIDRAW: "打开为 Excalidraw 绘图",
TOGGLE_MODE: "在 Excalidraw 和 Markdown 模式之间切换",
CONVERT_NOTE_TO_EXCALIDRAW: "转换空白笔记为 Excalidraw 绘图",
CONVERT_EXCALIDRAW: "转换 *.excalidraw 为 *.md 文件",
CREATE_NEW: "新建 Excalidraw 绘图",
CONVERT_FILE_KEEP_EXT: "*.excalidraw 格式 => *.excalidraw.md 格式",
CONVERT_FILE_REPLACE_EXT:
"*.excalidraw 格式 => *.md (Logseq compatibility) 格式",
CONVERT_FILE_REPLACE_EXT: "*.excalidraw 格式 => *.md (兼容 Logseq) 格式",
DOWNLOAD_LIBRARY: "导出 stencil 库为 *.excalidrawlib 文件",
OPEN_EXISTING_NEW_PANE: "在新面板中打开已存在的绘图",
OPEN_EXISTING_ACTIVE_PANE: "在当前面板中打开已存在的绘图",
TRANSCLUDE: "嵌入绘图",
TRANSCLUDE_MOST_RECENT: "嵌入最近编辑的绘图",
NEW_IN_NEW_PANE: "在新面板中创建已存在的绘图",
NEW_IN_ACTIVE_PANE: "在当前面板中创建已存在的绘图",
NEW_IN_NEW_PANE_EMBED: "在新面板中创建已存在的绘图且嵌入到当前笔记中",
NEW_IN_ACTIVE_PANE_EMBED: "在当前面板中创建已存在的绘图且嵌入到当前笔记中",
EXPORT_SVG: "导出 SVG 文件到当前文件的目录",
EXPORT_PNG: "导出 PNG 文件到当前文件的目录",
TOGGLE_LOCK: "切换文本元素锁定模式",
INSERT_LINK: "在文件中插入链接",
INSERT_LATEX: "在文件中插入 LaTeX 符号 (e.g. $\\theta$)",
ENTER_LATEX: "输入一个 LaTeX 表达式",
OPEN_EXISTING_NEW_PANE: "在新面板中打开已的绘图",
OPEN_EXISTING_ACTIVE_PANE: "在当前面板中打开已的绘图",
TRANSCLUDE: "嵌入绘图到该文档",
TRANSCLUDE_MOST_RECENT: "嵌入最近编辑的绘图到该文档",
NEW_IN_NEW_PANE: "在新面板中新建绘图",
NEW_IN_ACTIVE_PANE: "在当前面板中新建绘图",
NEW_IN_NEW_PANE_EMBED: "在新面板中新建绘图,并嵌入到当前文档",
NEW_IN_ACTIVE_PANE_EMBED: "在当前面板中新建绘图,并嵌入到当前文档",
EXPORT_SVG: "导出 SVG 文件到当前目录",
EXPORT_PNG: "导出 PNG 文件到当前目录",
TOGGLE_LOCK: "切换文本元素的原文/预览模式",
DELETE_FILE: "从 Obsidian 库文件夹中删除所选图像或 Markdown 文件",
INSERT_LINK: "插入链接到该绘图",
INSERT_IMAGE: "插入库文件夹里的图像到该绘图",
INSERT_MD: "将库文件夹里的 Markdown 文件以图像形式嵌入到该绘图",
INSERT_LATEX: "插入 LaTeX 公式到该绘图",
ENTER_LATEX:
"输入 LaTeX 表达式(示例: \\binom{n}{k} = \\frac{n!}{k!(n-k)!} ",
//ExcalidrawView.ts
INSTALL_SCRIPT_BUTTON: "安装或更新 Excalidraw 自动化脚本",
OPEN_AS_MD: "打开为 Markdown 文件",
SAVE_AS_PNG: "保存成 PNG 文件到库里(CTRL/CMD 加左键点击来指定导出位置)",
SAVE_AS_SVG: "保存成 SVG 文件到库里(CTRL/CMD 加左键点击来指定导出位置)",
OPEN_LINK: "以链接的方式打开文本 \n按住 SHIFT 来在新面板中打开)",
SAVE_AS_PNG: "导出 PNG 到当前目录(按住 CTRL/CMD 指定导出位置)",
SAVE_AS_SVG: "导出 SVG 到当前目录(按住 CTRL/CMD 指定导出位置)",
OPEN_LINK:
"将所选文本作为链接打开 \n按住 SHIFT 并点击此命令可在新面板打开)",
EXPORT_EXCALIDRAW: "导出为 .Excalidraw 文件",
LINK_BUTTON_CLICK_NO_TEXT:
"选择带有外部链接或部链接的文本。\n" +
"SHIFT 加左键点击按钮在新面板中打开链接。\n" +
"CTRL/CMD 加左键在画布中点击文本元素也可以打开对应的链接。",
TEXT_ELEMENT_EMPTY: "文本元素没有链接任何东西.",
FILENAME_INVALID_CHARS: '文件名不能包含以下符号: * " \\ < > : | ?',
"选择一个含有内部链接或部链接的图形或文本元素。\n" +
"按住 SHIFT 点击按钮在新面板中打开链接。\n" +
"也可以在画布中按住 CTRL/CMD 并点击图形或文本元素!",
TEXT_ELEMENT_EMPTY:
"未选中图形或文本元素,或者元素不包含有效的链接([[链接|缩写]] 或 [缩写](链接)",
FILENAME_INVALID_CHARS: '文件名不能含有以下符号: * " \\ < > : | ?',
FILE_DOES_NOT_EXIST:
"文件不存在。按住 ALT ALT + SHIFT加左键点击来创建新文件。",
FORCE_SAVE:
"强制保存以更新相邻面板中的嵌入。\n请注意自动保存始终处于开启状态",
RAW: "文本元素正以原文模式显示。 单击按钮更改为预览模式",
PARSED: "文本元素正以预览模式显示。 单击按钮更改为原文模式。",
NOFILE: "Excalidraw (没有文件)",
"文件不存在。按住 ALT或 ALT + SHIFT并点击链接来创建新文件。",
FORCE_SAVE: "强制保存并更新相邻面板。\n注意自动保存功能始终是开启的",
RAW: "含链接的文本元素正以原文模式显示。\n点击切换到预览模式",
PARSED: "含链接的文本元素正以预览模式显示。\n点击切换到原文模式",
NOFILE: "Excalidraw没有文件",
COMPATIBILITY_MODE:
"*.excalidraw 文件以兼容模式打开。转换为新格式以获得完整的插件功能。",
CONVERT_FILE: "转换为新格式",
//settings.ts
FOLDER_NAME: "Excalidraw 文件夹",
FOLDER_DESC: "新绘图的默认位置。如果此处为空,将在 Vault 根目录中创建绘图。",
FOLDER_DESC: "新绘图的默认位置。如果此处为空,将在库的根目录中创建绘图。",
TEMPLATE_NAME: "Excalidraw 模板文件",
TEMPLATE_DESC:
"Excalidraw 模板的完整文件路径。" +
"例:如果您的模板在默认的 Excalidraw 文件夹中且它的名称是" +
"Template.md你应当设置为:Excalidraw/Template.md。" +
"如果您在兼容模式下使用 Excalidraw那么您的模板也必须是旧的 excalidraw 文件," +
"Excalidraw 模板文件的完整路径。" +
"例:如果您的模板在默认的 Excalidraw 文件夹中且文件名是" +
"Template.md则此项应设为 Excalidraw/Template.md。" +
"如果您在兼容模式下使用 Excalidraw那么您的模板也必须是旧格式的 excalidraw 文件," +
"例如 Excalidraw/Template.excalidraw。",
SCRIPT_FOLDER_NAME: "Excalidraw 自动化脚本的文件夹",
SCRIPT_FOLDER_DESC:
"此文件夹用于存放 Excalidraw 自动化脚本。" +
"您可以在 Obsidian 命令面板中执行这些脚本。" +
"您可以为喜欢的脚本分配快捷键,就像为其他 Obsidian 命令分配快捷键一样。" +
"该项不能设为库的根目录。",
AUTOSAVE_NAME: "自动保存",
AUTOSAVE_DESC:
"每 30 秒自动保存编辑中的绘图。当您关闭 Excalidraw 或 Obsidian 或焦点移动到另一个面板时,通常会引发保存" +
"在极少数情况下自动保存可能会稍微扰乱绘图流程。我在创建此功能时考虑到了手机端(安卓)," +
"其中“滑到另一个应用程序”会导致一些数据丢失,并且因为我无法在手机上的应用程序" +
" 终止时强制保存。如果您在桌面上使用 Excalidraw这你可以关掉它。",
"每 30 秒自动保存编辑中的绘图。这个功能主要是为了防止在手机端(安卓)上的“滑动切换应用”操作可能" +
"导致的数据丢失,因为我无法在手机应用被终止时及时保存绘图。桌面端用户一般不需要开启此功能,因为" +
"在桌面端,只要关闭 Excalidraw 或 Obsidian或者移动焦点到其他面板就会自动触发一次保存开启" +
"此功能反而可能会干扰绘图流程。",
FILENAME_HEAD: "文件名",
FILENAME_DESC:
"<p>自动生成的文件名包括一个前缀和一个日期。" +
"<p>自动生成的文件名由前缀和日期两部分组成。" +
"例如 'Drawing 2021-05-24 12.58.07'。</p>" +
"<p>点击<a href='https://momentjs.com/docs/#/displaying/format/'>" +
"日期和时间格式参考</a>来查看如何修改。</p>",
"<p>可参考<a href='https://momentjs.com/docs/#/displaying/format/'>" +
"日期和时间格式说明</a>来进行修改。</p>",
FILENAME_SAMPLE: "当前文件名的格式为:<b>",
FILENAME_PREFIX_NAME: "文件名前缀",
FILENAME_PREFIX_DESC: "文件名的第一部分",
FILENAME_DATE_NAME: "文件名日期",
FILENAME_DATE_DESC: "文件名的第二部分",
LINKS_HEAD: "链接",
/*SVG_IN_MD_NAME: "SVG Snapshot to markdown file",
SVG_IN_MD_DESC: "If the switch is 'on' Excalidraw will include an SVG snapshot in the markdown file. "+
"When SVG snapshots are saved to the Excalidraw.md file, drawings that include large png, jpg, gif images may take extreme long time to open in markdown view. " +
"On the other hand, SVG snapshots provide some level of platform independence and longevity to your drawings. Even if Excalidraw will no longer exist, the snapshot " +
"can be opened with an app that reads SVGs. In addition hover previews will be less resource intensive if SVG snapshots are enabled.",*/
DISPLAY_HEAD: "显示",
MATCH_THEME_NAME: "使新建的绘图匹配 Obsidian 主题",
MATCH_THEME_DESC:
"如果 Obsidian 使用黑暗主题,新建的绘图文件也将使用黑暗主题。" +
"但如果设置了模板,新建的绘图文件将跟随模板主题。此功能不会作用于已有的绘图。",
MATCH_THEME_ALWAYS_NAME: "使已有的绘图匹配 Obsidian 主题",
MATCH_THEME_ALWAYS_DESC:
"如果 Obsidian 使用黑暗主题,则绘图文件也将以黑暗主题打开;反之亦然。",
MATCH_THEME_TRIGGER_NAME: "Excalidraw 主题跟随 Obsidian 主题变化",
MATCH_THEME_TRIGGER_DESC:
"开启此项,则更改 Obsidian 的黑暗/明亮主题时,当前打开的 Excalidraw 面板的主题会随之改变。",
DEFAULT_OPEN_MODE_NAME: "Excalidraw 的默认运行模式",
DEFAULT_OPEN_MODE_DESC:
"设置 Excalidraw 的运行模式:普通模式,真模式,或者阅读模式。你也可以为某个文件单独设置此项," +
"方法是在其 Frontmatter 中添加名为 excalidraw-default-mode 的键其值为normal, zen 或者 view。",
ZOOM_TO_FIT_NAME: "自动缩放以适应视图调整",
ZOOM_TO_FIT_DESC: "调整面板大小时,自适应地缩放画布",
ZOOM_TO_FIT_MAX_LEVEL_NAME: "自动缩放的最大级别",
ZOOM_TO_FIT_MAX_LEVEL_DESC:
"自动缩放画布时,允许放大的最高级别。该项不能低于 0.550%),不能超过 101000%)。",
LINKS_HEAD: "链接 & 嵌入到绘图中的文档",
LINKS_DESC:
"CTRL/CMD 加左键点击文本元素来打开链接。" +
"如果选中的文本指向多个双链,只会打开其中第一个。" +
"如果选中的文本为超链接 (i.e. https:// or http://)然后" +
"按住 CTRL/CMD 并点击包含 [[链接]] 的文本元素来打开链接。" +
"如果所选文本元素包含多个 [[链接]] ,只会打开第一个链接。" +
"如果所选文本元素包含 URL 链接 (如 https:// or http://)" +
"插件会在浏览器中打开超链接。" +
"当对应的文件名修改时,匹配的链接也会修改。" +
"如果你不希望你自己的链接文本突然修改,用别名来替代",
LINK_BRACKETS_NAME: "在链接上显示双链符号[[",
"链接的源文件被重命名时,绘图中相应的 [[链接]] 也会同步更新。" +
"若你不愿绘图中的链接文本因此而变化,可用 [[链接|别名]] 来替代",
ADJACENT_PANE_NAME: "在相邻面板中打开",
ADJACENT_PANE_DESC:
"按住 CTRL/CMD + SHIFT 并点击链接时,插件默认会在新面板中打开该链接。" +
"若开启此项Excalidraw 会先尝试寻找已有的相邻面板(按照右侧、左侧、上方、下方的顺序)," +
"并在其中打开链接。如果找不到," +
"再在新面板中打开链接。",
LINK_BRACKETS_NAME: "在链接两侧显示 [[中括号]]",
LINK_BRACKETS_DESC: `${
"预览(锁定)模式,当解析文本元素,在链接左右展示中括号。" +
"可以在文件的 Frontmatter 中加入'"
}${FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS}: true/false' 来单独控制某个文件`,
"预览模式下,渲染文本元素,在链接两侧显示中括号。" +
"可以在绘图文件的 Frontmatter 中加入 '"
}${FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS}: true/false' 来为其单独设置此项`,
LINK_PREFIX_NAME: "链接前缀",
LINK_PREFIX_DESC: `${
"预览(锁定)模式,如果文本元素包含链接,在文本之前加上这些字符。" +
"可以在文件的 Frontmatter 中加入 '"
}${FRONTMATTER_KEY_CUSTOM_PREFIX}: "👉 "' 单独更改`,
LINK_CTRL_CLICK_NAME: "CTRL/CMD 加左键点击文本来打开链接",
"预览模式,如果文本元素包含链接,则添加这些字符作为前缀。" +
"可以在绘图文件的 Frontmatter 中加入 '"
}${FRONTMATTER_KEY_CUSTOM_PREFIX}: "📍 "' 来为其单独设置此项。`,
URL_PREFIX_NAME: "URL 前缀",
URL_PREFIX_DESC: `${
"预览模式下,如果文本元素包含 URL 链接,则为其添加这些字符作为前缀。" +
"您可以在绘图文件的 Frontmatter 中加入 '"
}${FRONTMATTER_KEY_CUSTOM_URL_PREFIX}: "🌐 "' 来为其单独设置此项。`,
LINK_CTRL_CLICK_NAME:
"按住 CTRL/CMD 并点击含有 [[链接]] 或 [ ](链接) 的文本来打开链接",
LINK_CTRL_CLICK_DESC:
"如果此功能干扰了您要使用的 Excalidraw 功能,您可以将其关闭。 如果" +
"关闭此选项,则只有绘图标题栏中的链接按钮可以让你打开链接。",
EMBED_HEAD: "嵌入 & 导出",
EMBED_WIDTH_NAME: "嵌入图像的默认宽度",
"如果此功能影响到您使用某些原版 Excalidraw 功能,将其关闭。" +
"关闭后,您只能通过绘图面板标题栏中的链接按钮打开链接。",
TRANSCLUSION_WRAP_NAME: "嵌入到绘图中的文档的折行方式",
TRANSCLUSION_WRAP_DESC:
"中的 number 表示嵌入的文本溢出时,在第几个字符处进行折行。" +
"此开关控制具体的折行方式。若开启,则严格在 number 处折行,禁止溢出;" +
"若关闭,则允许在 number 位置后最近的空格处折行。",
PAGE_TRANSCLUSION_CHARCOUNT_NAME: "嵌入到绘图中的文档的最大显示字符数",
PAGE_TRANSCLUSION_CHARCOUNT_DESC:
"以 ![[Markdown文档]] 的形式将文档嵌入到绘图中时," +
"该文档在绘图中可显示的最大字符数量。",
GET_URL_TITLE_NAME: "使用 iframly 获取页面标题",
GET_URL_TITLE_DESC:
"拖放链接到 Excalidraw 时,使用 http://iframely.server.crestify.com/iframely?url= 来获取页面的标题",
MD_HEAD: "以图像形式嵌入到绘图中的 Markdown 文档",
MD_HEAD_DESC:
"你可以将 Markdown 文档以图像的形式嵌入到绘图中," +
"方法是按住 CTRL/CMD 并从文件管理器中把文档拖入绘图,或者使用命令面板。",
MD_TRANSCLUDE_WIDTH_NAME: "以图像形式嵌入到绘图中的 Markdown 文档的默认宽度",
MD_TRANSCLUDE_WIDTH_DESC:
"以图像形式嵌入到绘图中的 Markdown 文档的宽度。该选项会影响到折行,以及生成的图像元素的宽度。" +
"您可以将绘图打开为 Markdown 文件,用 [[文档名#标题|宽度x最大高度]] 的形式," +
"来单独为该嵌入的文档设定此项。",
MD_TRANSCLUDE_HEIGHT_NAME:
"以图像形式嵌入到绘图中的 Markdown 文档的默认最大高度",
MD_TRANSCLUDE_HEIGHT_DESC:
"以图像形式嵌入到绘图中的 Markdown 文档产生的图像高度取决于文档内容的多少,但最大不会超过该值。" +
"您可以将绘图打开为 Markdown 文件,用 [[文档名#^块引ID|宽度x最大高度]] 的形式,来单独为该嵌入的文档设定此项。",
MD_DEFAULT_FONT_NAME: "以图像形式嵌入到绘图中的 Markdown 文档的默认字体",
MD_DEFAULT_FONT_DESC:
"可以设为 VirgilCasadia 或其他 .ttf/.woff/.woff2 字体文件(如 MyFont.woff2。" +
'您可以在该 Markdown 文档的 Frontmatter 中添加形如 "excalidraw-font: 字体或文件名" 的键值对,来为其单独设定此项。',
MD_DEFAULT_COLOR_NAME: "以图像形式嵌入到绘图中的 Markdown 文档的默认文本颜色",
MD_DEFAULT_COLOR_DESC:
"设为 css 颜色名,如 steelblue参考 https://www.w3schools.com/colors/colors_names.asp或者有效的 16 进制颜色值,例如 #e67700。</p>" +
'您可以在该 Markdown 文档的 Frontmatter 中添加形如 "excalidraw-font-color: 颜色名或颜色值" 的键值对,来为其单独设定此项。',
MD_CSS_NAME: "CSS 文件",
MD_CSS_DESC:
"以图像形式嵌入 Markdown 文档到绘图中时所使用的 CSS 文件名。需包含扩展名,例如 md-embed.css。" +
"也可以使用 Markdown 文件(如 md-embed-css.md但其内容应符合 CSS 语法。" +
"如果您要查询 CSS 所作用的 HTML 节点,请在 Obsidian 开发者控制台CTRL+SHIFT+i中键入命令" +
'"ExcalidrawAutomate.mostRecentMarkdownSVG" —— 这将显示 Excalidraw 最近生成的 SVG。' +
"此外,在 CSS 中不能任意地设置字体,您一般只能使用系统默认的标准字体(详见 README" +
"但可以通过前面的设置来额外添加一个自定义字体。" +
'您可以在该 Markdown 文档的 Frontmatter 中添加形如 "excalidraw-css: 库中的CSS文件或CSS片段" 的键值对,来为其单独设定此项。',
EMBED_HEAD: "嵌入到文档中的绘图 & 导出",
EMBED_PREVIEW_SVG_NAME: "预览图采用 SVG 格式",
EMBED_PREVIEW_SVG_DESC:
"默认情况下 Obsidian 的 Markdown 阅读视图会将嵌入到文档中的绘图显示为 SVG 格式的预览图。关闭此项,则显示为 PNG 格式。",
PREVIEW_MATCH_OBSIDIAN_NAME: "预览图匹配 Obsidian 主题",
PREVIEW_MATCH_OBSIDIAN_DESC:
"开启此项,则当 Obsidian 处于黑暗模式时,嵌入到文档中的绘图的预览图也会以黑暗模式渲染;" +
"当 Obsidian 处于明亮模式时,嵌入到文档中的绘图的预览图也会以明亮模式渲染。您可能还需要关闭“导出的图像包含背景”开关,来获得与 Obsidian 更加协调的观感。",
EMBED_WIDTH_NAME: "预览图的默认宽度",
EMBED_WIDTH_DESC:
"嵌入图形的默认宽度。您可以在使用" +
"![[drawing.excalidraw|100]] 或 [[drawing.excalidraw|100x100]]" +
"格式指定嵌入图像时的宽度。",
"嵌入到文档中的绘图的预览图的默认宽度。此项仅作用于嵌入到文档中的 excalidraw 绘图,不影响嵌入到文档中的其他图像。" +
"您可以用 ![[drawing.excalidraw|100]] 或 [[drawing.excalidraw|100x100]]" +
"的形式来为某幅绘图的单独设定预览图宽度。",
EMBED_TYPE_NAME: "绘图嵌入到文档时的类型",
EMBED_TYPE_DESC:
"通过命令面板嵌入绘图时,要嵌入原始的绘图文件,还是嵌入 PNG 或 SVG 副本。" +
"你需要开启下方的“自动导出 PNG/SVG”等开关才能在该下拉框中选择 PNG 或 SVG 副本。" +
"如果绘图文件缺少对应的 PNG 或 SVG 副本,命令面板将会插入一条损坏的链接,这时你需要打开原始绘图并手动导出副本 —— " +
"该选项不会自动生成 PNG/SVG 副本,只会引用已经存在的 PNG/SVG 副本。",
EXPORT_PNG_SCALE_NAME: "PNG 导出图像比例",
EXPORT_PNG_SCALE_DESC: "导出的 PNG 图像的大小比例",
EXPORT_BACKGROUND_NAME: "导出带有背景的图像",
EXPORT_BACKGROUND_DESC: "如果关闭,导出的图像的背景将是透明的。",
EXPORT_THEME_NAME: "导出带有主题的图像",
EXPORT_BACKGROUND_NAME: "导出的图像包含背景",
EXPORT_BACKGROUND_DESC: "如果关闭,导出透明背景的图像。",
EXPORT_THEME_NAME: "导出的图像包含主题",
EXPORT_THEME_DESC:
"导出与绘图的暗/亮主题匹配的图像。" +
"如果关闭,在深色模式下导出的绘图将和浅色模式下导出的图像一样",
"导出与绘图的暗/亮主题匹配的图像。" +
"如果关闭,在黑暗主题下导出的图像将和明亮主题一样",
EXPORT_HEAD: "导出设置",
EXPORT_SYNC_NAME: "保持 .SVG 和/或 .PNG 文件名与绘图文件同步",
EXPORT_SYNC_NAME: "保持 .SVG 和 .PNG 文件名与绘图文件同步",
EXPORT_SYNC_DESC:
"打开后,当同一文件夹且同名的绘图被重命名时,插件将自动更新对应的 .SVG 和/或 .PNG 文件的文件名。" +
"当同一文件夹的同一名称的绘图被删除时,插件将自动删除对应的 .SVG 和/或 .PNG 文件。",
"打开后,当绘图文件被重命名时,插件将同步更新同文件夹下的同名 .SVG 和 .PNG 文件。" +
"当绘图文件被删除时,插件将自动删除同文件夹下的同名 .SVG 和 .PNG 文件。",
EXPORT_SVG_NAME: "自动导出 SVG",
EXPORT_SVG_DESC:
"自动导出和文件同名的 SVG 文件" +
"插件会将 SVG 文件保存到对应的 Excalidraw 所在的文件夹中" +
"将 .svg 文件嵌入到文档中,而不是 excalidraw使您嵌入的页面独立开来" +
"当自动导出开关打开时,每次您编辑对应的 excalidraw 绘图时,此文件都会更新。",
"自动导出和绘图文件同名的 SVG 文件" +
"插件会将 SVG 文件保存到绘图文件所在的文件夹中" +
"在文档中嵌入这个 SVG 文件,相比直接嵌入绘图文件,具有更强的跨平台能力。" +
"此开关开启时,每次您编辑 excalidraw 绘图,相应的 SVG 文件都会同步更新。",
EXPORT_PNG_NAME: "自动导出 PNG",
EXPORT_PNG_DESC: "自动导出 SVG 一样,但面向 *.PNG",
EXPORT_PNG_DESC: "类似于自动导出 SVG,但导出格式为 *.PNG",
COMPATIBILITY_HEAD: "兼容特性",
EXPORT_EXCALIDRAW_NAME: "自动导出 Excalidraw 文件",
EXPORT_EXCALIDRAW_DESC: "自动导出 SVG 一样,但面向 *.Excalidraw",
SYNC_EXCALIDRAW_NAME: "同步 .md 格式以及 .excalidraw 格式",
EXPORT_EXCALIDRAW_DESC: "类似于自动导出 SVG,但导出格式为 *.Excalidraw",
SYNC_EXCALIDRAW_NAME: "同步同一绘图的两种格式",
SYNC_EXCALIDRAW_DESC:
"如果 *.excalidraw 文件的修改比 *.md 文件的修改更新" +
",会根据 .excalidraw 文件更新 .md 文件中的绘图",
"如果 *.excalidraw 格式文件的修改日期比 *.md 格式文件更新" +
"根据 .excalidraw 文件更新 .md 文件中的绘图",
COMPATIBILITY_MODE_NAME: "以旧格式创建新绘图",
COMPATIBILITY_MODE_DESC:
"启此功能后,你使用功能区图标、命令面板、" +
"文件浏览器创建的绘图都将是旧格式 *.excalidraw 文件。 此设置还将" +
"关闭你打开并编辑旧格式绘图文件时提醒消息",
EXPERIMENTAL_HEAD: "实验性特性",
"启此功能后,您通过功能区按钮、命令面板、" +
"文件浏览器创建的绘图都将是旧 *.excalidraw 格式。此外," +
"当您打开旧格式绘图文件时将不再收到提醒消息",
EXPERIMENTAL_HEAD: "实验性功能",
EXPERIMENTAL_DESC:
"这些设置不会立即生效,只有在刷新文件资源管理器或重新启动 Obsidian 才会生效。",
FILETYPE_NAME: "在文件浏览器中给所有的 Excalidraw 文件加上 ✏️ 标识符",
FILETYPE_DESC:
"Excalidraw 文件将使用下一个设置中定义的表情符号或文本来做标识",
FILETAG_NAME: "给 Excalidraw 文件设置标识符",
FILETAG_DESC: "要显示为标识符的文本或表情符号。",
"这些设置不会立即生效,需要刷新文件资源管理器或重新启动 Obsidian 才会生效。",
FILETYPE_NAME: "在文件浏览器中 Excalidraw 绘图文件添加类型标识符(如 ✏️)",
FILETYPE_DESC: "可通过下一项设置来自定义类型标识符。",
FILETAG_NAME: "设置 Excalidraw 绘图文件的类型标识",
FILETAG_DESC: "要显示为类型标识符的 emoji 或文本。",
INSERT_EMOJI: "插入 emoji",
LIVEPREVIEW_NAME: "在实时预览编辑模式中,嵌入到文档中的绘图以图像的方式渲染",
LIVEPREVIEW_DESC:
"开启此项,则可在实时预览编辑模式中,用形如 ![[绘图|宽度|样式]] 的方式来嵌入绘图。" +
"该选项不会在已打开的文档中立刻生效 —— " +
"你需要重新打开此文档来使其生效。",
ENABLE_FOURTH_FONT_NAME: "为文本元素启用本地字体",
ENABLE_FOURTH_FONT_DESC:
"开启此项后,文本元素的属性面板里会多出一个本地字体按钮。" +
"使用了本地字体的绘图文件,将会失去一部分跨平台能力 —— " +
"若将绘图文件移动到其他库中打开,显示效果可能会截然不同;" +
"若在 excalidraw.com 或者其他版本的 Excalidraw 中打开,使用本地字体的文本会变回系统默认字体。",
FOURTH_FONT_NAME: "本地字体文件",
FOURTH_FONT_DESC:
"选择库文件夹中的一个 .ttf, .woff 或 .woff2 字体文件作为本地字体文件。" +
"若未选择文件,则使用默认的 Virgil 字体。",
//openDrawings.ts
SELECT_FILE: "选择一个文件后按回车。",
NO_MATCH: "没有文件匹配你的索引。",
SELECT_FILE_TO_LINK: "选择要为其插入链接的文件。",
NO_MATCH: "无法匹配到你所查询的文件。",
SELECT_FILE_TO_LINK: "选择要插入链接的文件。",
SELECT_DRAWING: "选择想要插入的绘图",
TYPE_FILENAME: "键入要选择的绘图名称。",
SELECT_FILE_OR_TYPE_NEW: "选择有绘图新绘图的类型名称,然后按回车。",
SELECT_FILE_OR_TYPE_NEW: "选择有绘图,或者新绘图的类型,然后按回车。",
SELECT_TO_EMBED: "选择要插入到当前文档中的绘图。",
SELECT_MD: "选择想要插入的 Markdown 文档",
//EmbeddedFileLoader.ts
INFINITE_LOOP_WARNING:
"EXCALIDRAW 警告\n停止加载嵌入的图像因为此文件中存在死循环\n",
};

View File

@@ -7,7 +7,6 @@ import {
PluginManifest,
MarkdownView,
normalizePath,
MarkdownPostProcessorContext,
Menu,
MenuItem,
TAbstractFile,
@@ -39,8 +38,10 @@ import {
CTRL_OR_CMD,
SCRIPT_INSTALL_CODEBLOCK,
SCRIPT_INSTALL_FOLDER,
VIRGIL_FONT,
VIRGIL_DATAURL,
} from "./constants";
import ExcalidrawView, { ExportSettings, TextMode } from "./ExcalidrawView";
import ExcalidrawView, { TextMode } from "./ExcalidrawView";
import { getMarkdownDrawingSection } from "./ExcalidrawData";
import {
ExcalidrawSettings,
@@ -55,8 +56,6 @@ import {
initExcalidrawAutomate,
destroyExcalidrawAutomate,
ExcalidrawAutomate,
createSVG,
createPNG,
} from "./ExcalidrawAutomate";
import { Prompt } from "./Prompt";
import { around } from "monkey-around";
@@ -64,20 +63,23 @@ import { t } from "./lang/helpers";
import {
checkAndCreateFolder,
download,
embedFontsInSVG,
errorlog,
getAttachmentsFolderAndFilePath,
getIMGFilename,
getFontDataURL,
getIMGPathFromExcalidrawFile,
getNewUniqueFilepath,
isObsidianThemeDark,
log,
svgToBase64,
} from "./Utils";
import { OneOffs } from "./OneOffs";
import { FileId } from "@zsviczian/excalidraw/types/element/types";
import { EmbeddedFilesLoader } from "./EmbeddedFileLoader";
import { ScriptEngine } from "./Scripts";
import {
hoverEvent,
initializeMarkdownPostProcessor,
markdownPostProcessor,
observer,
} from "./MarkdownPostProcessor";
declare module "obsidian" {
interface App {
@@ -118,7 +120,7 @@ export default class ExcalidrawPlugin extends Plugin {
public mathjax: any = null;
private mathjaxDiv: HTMLDivElement = null;
public scriptEngine: ScriptEngine;
public fourthFontDef: string = VIRGIL_FONT;
constructor(app: App, manifest: PluginManifest) {
super(app, manifest);
this.filesMaster = new Map<
@@ -153,6 +155,7 @@ export default class ExcalidrawPlugin extends Plugin {
this.experimentalFileTypeDisplayToggle(this.settings.experimentalFileType);
this.registerCommands();
this.registerEventListeners();
this.initializeFourthFont();
//inspiration taken from kanban:
//https://github.com/mgmeyers/obsidian-kanban/blob/44118e25661bff9ebfe54f71ae33805dc88ffa53/src/main.ts#L267
@@ -184,6 +187,37 @@ export default class ExcalidrawPlugin extends Plugin {
});
}
public initializeFourthFont() {
this.app.workspace.onLayoutReady(async () => {
const font = await getFontDataURL(
this.app,
this.settings.experimantalFourthFont,
"",
"LocalFont",
);
const fourthFontDataURL =
font.dataURL === "" ? VIRGIL_DATAURL : font.dataURL;
this.fourthFontDef = font.fontDef;
const newStylesheet = document.createElement("style");
newStylesheet.id = "local-font-stylesheet";
newStylesheet.textContent = `
@font-face {
font-family: 'LocalFont';
src: url("${fourthFontDataURL}");
font-display: swap;
}
`;
// replace the old local font <style> element with the one we just created
const oldStylesheet = document.getElementById(newStylesheet.id);
document.head.appendChild(newStylesheet);
if (oldStylesheet) {
document.head.removeChild(oldStylesheet);
}
await (document as any).fonts.load(`20px LocalFont`);
});
}
private loadMathJax() {
//loading Obsidian MathJax as fallback
this.app.workspace.onLayoutReady(() => {
@@ -227,40 +261,45 @@ export default class ExcalidrawPlugin extends Plugin {
}
});
}
private registerInstallCodeblockProcessor() {
const codeblockProcessor = async (
source: string,
el: HTMLElement,
ctx: MarkdownPostProcessorContext,
plugin: ExcalidrawPlugin,
) => {
private registerInstallCodeblockProcessor() {
const codeblockProcessor = async (source: string, el: HTMLElement) => {
//Button next to the "List of available scripts" at the top
//In try/catch block because this approach is very error prone, depends on
//In try/catch block because this approach is very error prone, depends on
//MarkdownRenderer() and index.md structure, in case these are not as
//expected this code will break
let button2: HTMLButtonElement = null;
try {
const link:HTMLElement = el.parentElement.querySelector(`a[href="#${
el.previousElementSibling.getAttribute("data-heading")}"]`);
const link: HTMLElement = el.parentElement.querySelector(
`a[href="#${el.previousElementSibling.getAttribute(
"data-heading",
)}"]`,
);
link.style.paddingRight = "10px";
button2 = link.parentElement.createEl("button",null,(b) => {
button2 = link.parentElement.createEl("button", null, (b) => {
b.setText(t("UPDATE_SCRIPT"));
b.addClass("mod-cta");
b.style.backgroundColor = "var(--interactive-success)";
b.style.display = "none";
});
} catch(e) {
errorlog({where:"this.registerInstallCodeblockProcessor", source, error:e});
} catch (e) {
errorlog({
where: "this.registerInstallCodeblockProcessor",
source,
error: e,
});
}
source = source.trim();
el.createEl("button", null, async (button) => {
const setButtonText = (text:"CHECKING" | "INSTALL" | "UPTODATE" | "UPDATE" | "ERROR") => {
if(button2) button2.style.display = "none";
switch(text) {
case "CHECKING":
const setButtonText = (
text: "CHECKING" | "INSTALL" | "UPTODATE" | "UPDATE" | "ERROR",
) => {
if (button2) {
button2.style.display = "none";
}
switch (text) {
case "CHECKING":
button.setText(t("CHECKING_SCRIPT"));
button.style.backgroundColor = "var(--interactive-normal)";
break;
@@ -275,75 +314,80 @@ export default class ExcalidrawPlugin extends Plugin {
case "UPDATE":
button.setText(t("UPDATE_SCRIPT"));
button.style.backgroundColor = "var(--interactive-success)";
if(button2) button2.style.display = null;
if (button2) {
button2.style.display = null;
}
break;
case "ERROR":
button.setText(t("UNABLETOCHECK_SCRIPT"));
button.style.backgroundColor = "var(--interactive-normal)";
break;
}
}
};
button.addClass("mod-cta");
let decodedURI = source;
try{
try {
decodedURI = decodeURI(source);
} catch(e) {
} catch (e) {
errorlog({
where:"ExcalidrawPlugin.registerInstallCodeblockProcessor.codeblockProcessor.onClick",
where:
"ExcalidrawPlugin.registerInstallCodeblockProcessor.codeblockProcessor.onClick",
source,
error:e,
error: e,
});
}
const fname = decodedURI.substring(
decodedURI.lastIndexOf("/") + 1,
);
const folder = `${
this.settings.scriptFolderPath
}/${SCRIPT_INSTALL_FOLDER}`;
const fname = decodedURI.substring(decodedURI.lastIndexOf("/") + 1);
const folder = `${this.settings.scriptFolderPath}/${SCRIPT_INSTALL_FOLDER}`;
const path = `${folder}/${fname}`;
let f = this.app.vault.getAbstractFileByPath(path);
setButtonText(f?"CHECKING":"INSTALL");
setButtonText(f ? "CHECKING" : "INSTALL");
button.onclick = async () => {
try {
const data = await request({url:source});
if(f) {
await this.app.vault.modify(f as TFile,data);
const data = await request({ url: source });
if (f) {
await this.app.vault.modify(f as TFile, data);
} else {
await checkAndCreateFolder(this.app.vault,folder);
f = await this.app.vault.create(path,data);
}
await checkAndCreateFolder(this.app.vault, folder);
f = await this.app.vault.create(path, data);
}
setButtonText("UPTODATE");
new Notice(`Installed: ${(f as TFile).basename}`)
new Notice(`Installed: ${(f as TFile).basename}`);
} catch (e) {
new Notice(`Error installing script: ${fname}`);
errorlog({
where:"ExcalidrawPlugin.registerInstallCodeblockProcessor.codeblockProcessor.onClick",
error:e,
where:
"ExcalidrawPlugin.registerInstallCodeblockProcessor.codeblockProcessor.onClick",
error: e,
});
}
};
if(button2) button2.onclick = button.onclick;
if (button2) {
button2.onclick = button.onclick;
}
//check modified date on github
//https://superuser.com/questions/1406875/how-to-get-the-latest-commit-date-of-a-file-from-a-given-github-reposotiry
if(!f || !(f instanceof TFile)) return;
const msgHead = "https://api.github.com/repos/zsviczian/obsidian-excalidraw-plugin/commits?path=ea-scripts%2F";
if (!f || !(f instanceof TFile)) {
return;
}
const msgHead =
"https://api.github.com/repos/zsviczian/obsidian-excalidraw-plugin/commits?path=ea-scripts%2F";
const msgTail = "&page=1&per_page=1";
const data = await request({
url: msgHead+encodeURI(fname)+msgTail,
url: msgHead + encodeURI(fname) + msgTail,
});
if(!data) {
if (!data) {
setButtonText("ERROR");
return;
}
const result = JSON.parse(data);
if(result.length===0 || !result[0]?.commit?.committer?.date) {
if (result.length === 0 || !result[0]?.commit?.committer?.date) {
setButtonText("ERROR");
return;
}
//@ts-ignore
const mtime = (new Date(result[0].commit.committer.date))/1;
if(mtime > f.stat.mtime) {
const mtime = new Date(result[0].commit.committer.date) / 1;
if (mtime > f.stat.mtime) {
setButtonText("UPDATE");
return;
}
@@ -353,13 +397,13 @@ export default class ExcalidrawPlugin extends Plugin {
this.registerMarkdownCodeBlockProcessor(
SCRIPT_INSTALL_CODEBLOCK,
async (source, el, ctx) => {
async (source, el) => {
el.addEventListener(RERENDER_EVENT, async (e) => {
e.stopPropagation();
el.empty();
codeblockProcessor(source, el, ctx, this);
codeblockProcessor(source, el);
});
codeblockProcessor(source, el, ctx, this);
codeblockProcessor(source, el);
},
);
}
@@ -368,380 +412,14 @@ export default class ExcalidrawPlugin extends Plugin {
* Displays a transcluded .excalidraw image in markdown preview mode
*/
private addMarkdownPostProcessor() {
interface imgElementAttributes {
file?: TFile;
fname: string; //Excalidraw filename
fwidth: string; //Display width of image
fheight: string; //Display height of image
style: string; //css style to apply to IMG element
}
/**
* Generates an img element with the drawing encoded as a base64 SVG or a PNG (depending on settings)
* @param parts {imgElementAttributes} - display properties of the image
* @returns {Promise<HTMLElement>} - the IMG HTML element containing the image
*/
const getIMG = async (
imgAttributes: imgElementAttributes,
): Promise<HTMLElement> => {
let file = imgAttributes.file;
if (!imgAttributes.file) {
const f = this.app.vault.getAbstractFileByPath(imgAttributes.fname);
if (!(f && f instanceof TFile)) {
return null;
}
file = f;
}
const exportSettings: ExportSettings = {
withBackground: this.settings.exportWithBackground,
withTheme: this.settings.exportWithTheme,
};
const img = createEl("img");
let style = `max-width:${imgAttributes.fwidth}px !important; width:100%;`;
if (imgAttributes.fheight) {
style += `height:${imgAttributes.fheight}px;`;
}
img.setAttribute("style", style);
img.addClass(imgAttributes.style);
const theme = this.settings.previewMatchObsidianTheme
? isObsidianThemeDark()
? "dark"
: "light"
: !this.settings.exportWithTheme
? "light"
: undefined;
if (theme) {
exportSettings.withTheme = true;
}
const loader = new EmbeddedFilesLoader(
this,
theme ? theme === "dark" : undefined,
);
if (!this.settings.displaySVGInPreview) {
const width = parseInt(imgAttributes.fwidth);
let scale = 1;
if (width >= 600) {
scale = 2;
}
if (width >= 1200) {
scale = 3;
}
if (width >= 1800) {
scale = 4;
}
if (width >= 2400) {
scale = 5;
}
const png = await createPNG(
file.path,
scale,
exportSettings,
loader,
theme,
null,
null,
[],
this,
);
//const png = await getPNG(JSON_parse(scene),exportSettings, scale);
if (!png) {
return null;
}
img.src = URL.createObjectURL(png);
return img;
}
const svgSnapshot = (
await createSVG(
file.path,
true,
exportSettings,
loader,
theme,
null,
null,
[],
this,
)
).outerHTML;
let svg: SVGSVGElement = null;
const el = document.createElement("div");
el.innerHTML = svgSnapshot;
const firstChild = el.firstChild;
if (firstChild instanceof SVGSVGElement) {
svg = firstChild;
}
if (!svg) {
return null;
}
svg = embedFontsInSVG(svg);
svg.removeAttribute("width");
svg.removeAttribute("height");
img.setAttribute("src", svgToBase64(svg.outerHTML));
return img;
};
const createImageDiv = async (
attr: imgElementAttributes,
): Promise<HTMLDivElement> => {
const img = await getIMG(attr);
return createDiv(attr.style, (el) => {
el.append(img);
el.setAttribute("src", attr.file.path);
if (attr.fwidth) {
el.setAttribute("w", attr.fwidth);
}
if (attr.fheight) {
el.setAttribute("h", attr.fheight);
}
el.onClickEvent((ev) => {
if (
ev.target instanceof Element &&
ev.target.tagName.toLowerCase() != "img"
) {
return;
}
const src = el.getAttribute("src");
if (src) {
this.openDrawing(
this.app.vault.getAbstractFileByPath(src) as TFile,
ev[CTRL_OR_CMD],
);
} //.ctrlKey||ev.metaKey);
});
el.addEventListener(RERENDER_EVENT, async (e) => {
e.stopPropagation();
el.empty();
const img = await getIMG({
fname: el.getAttribute("src"),
fwidth: el.getAttribute("w"),
fheight: el.getAttribute("h"),
style: el.getAttribute("class"),
});
el.append(img);
});
});
};
const tmpObsidianWYSIWYG = async (
el: HTMLElement,
ctx: MarkdownPostProcessorContext,
) => {
if (!ctx.frontmatter) {
return;
}
if (!ctx.frontmatter.hasOwnProperty("excalidraw-plugin")) {
return;
}
//@ts-ignore
if (ctx.remainingNestLevel < 4) {
return;
}
if (!el.querySelector(".frontmatter")) {
el.style.display = "none";
return;
}
const attr: imgElementAttributes = {
fname: ctx.sourcePath,
fheight: "",
fwidth: this.settings.width,
style: "excalidraw-svg",
};
attr.file = this.app.metadataCache.getFirstLinkpathDest(
ctx.sourcePath,
"",
);
const div = await createImageDiv(attr);
el.childNodes.forEach(
(child: HTMLElement) => (child.style.display = "none"),
);
el.appendChild(div);
};
/**
*
* @param el
* @param ctx
*/
const markdownPostProcessor = async (
el: HTMLElement,
ctx: MarkdownPostProcessorContext,
) => {
//check to see if we are rendering in editing mode of live preview
//if yes, then there should be no .internal-embed containers
const embeddedItems = el.querySelectorAll(".internal-embed");
if (embeddedItems.length === 0) {
tmpObsidianWYSIWYG(el, ctx);
return;
}
//If the file being processed is an excalidraw file,
//then I want to hide all embedded items as these will be
//transcluded text element or some other transcluded content inside the Excalidraw file
//in reading mode these elements should be hidden
if (ctx.frontmatter?.hasOwnProperty("excalidraw-plugin")) {
el.style.display = "none";
return;
}
//if not, then we are processing a non-excalidraw file in reading mode
//in that cases embedded files will be displayed in an .internal-embed container
const attr: imgElementAttributes = {
fname: "",
fheight: "",
fwidth: "",
style: "",
};
let alt: string;
let parts;
let file: TFile;
//Iterating through all the containers to check which one is an excalidraw drawing
//This is a for loop instead of embeddedItems.forEach() because createImageDiv at the end
//is awaited, otherwise excalidraw images would not display in the Kanban plugin
for (const maybeDrawing of embeddedItems) {
//check to see if the file in the src attribute exists
attr.fname = maybeDrawing.getAttribute("src");
file = this.app.metadataCache.getFirstLinkpathDest(
attr.fname?.split("#")[0],
ctx.sourcePath,
);
//if the embeddedFile exits and it is an Excalidraw file
//then lets replace the .internal-embed with the generated PNG or SVG image
if (file && file instanceof TFile && this.isExcalidrawFile(file)) {
attr.fwidth = maybeDrawing.getAttribute("width")
? maybeDrawing.getAttribute("width")
: this.settings.width;
attr.fheight = maybeDrawing.getAttribute("height");
alt = maybeDrawing.getAttribute("alt");
if (alt == attr.fname) {
alt = "";
} //when the filename starts with numbers followed by a space Obsidian recognizes the filename as alt-text
attr.style = "excalidraw-svg";
if (alt) {
//for some reason Obsidian renders ![]() in a DIV and ![[]] in a SPAN
//also the alt-text of the DIV does not include the alt-text of the image
//thus need to add an additional "|" character when its a SPAN
if (maybeDrawing.tagName.toLowerCase() == "span") {
alt = `|${alt}`;
}
//1:width, 2:height, 3:style 1 2 3
parts = alt.match(/[^\|]*\|?(\d*%?)x?(\d*%?)\|?(.*)/);
attr.fwidth = parts[1] ? parts[1] : this.settings.width;
attr.fheight = parts[2];
if (parts[3] != attr.fname) {
attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`;
}
}
attr.fname = file?.path;
attr.file = file;
const div = await createImageDiv(attr);
maybeDrawing.parentElement.replaceChild(div, maybeDrawing);
}
}
};
initializeMarkdownPostProcessor(this);
this.registerMarkdownPostProcessor(markdownPostProcessor);
/**
* internal-link quick preview
* @param e
* @returns
*/
const hoverEvent = (e: any) => {
if (!e.linktext) {
this.hover.linkText = null;
return;
}
this.hover.linkText = e.linktext;
this.hover.sourcePath = e.sourcePath;
};
// internal-link quick preview
this.registerEvent(this.app.workspace.on("hover-link", hoverEvent));
//monitoring for div.popover.hover-popover.file-embed.is-loaded to be added to the DOM tree
this.observer = new MutationObserver(async (m) => {
if (m.length == 0) {
return;
}
if (!this.hover.linkText) {
return;
}
const file = this.app.metadataCache.getFirstLinkpathDest(
this.hover.linkText,
this.hover.sourcePath ? this.hover.sourcePath : "",
);
if (!file) {
return;
}
if (!(file instanceof TFile)) {
return;
}
if (file.extension !== "excalidraw") {
return;
}
const svgFileName = getIMGFilename(file.path, "svg");
const svgFile = this.app.vault.getAbstractFileByPath(svgFileName);
if (svgFile && svgFile instanceof TFile) {
return;
} //If auto export SVG or PNG is enabled it will be inserted at the top of the excalidraw file. No need to manually insert hover preview
const pngFileName = getIMGFilename(file.path, "png");
const pngFile = this.app.vault.getAbstractFileByPath(pngFileName);
if (pngFile && pngFile instanceof TFile) {
return;
} //If auto export SVG or PNG is enabled it will be inserted at the top of the excalidraw file. No need to manually insert hover preview
if (!this.hover.linkText) {
return;
}
if (m.length != 1) {
return;
}
if (m[0].addedNodes.length != 1) {
return;
}
if (
//@ts-ignore
m[0].addedNodes[0].className !=
"popover hover-popover file-embed is-loaded"
) {
return;
}
const node = m[0].addedNodes[0];
node.empty();
//this div will be on top of original DIV. By stopping the propagation of the click
//I prevent the default Obsidian feature of openning the link in the native app
const img = await getIMG({
file,
fname: file.path,
fwidth: "300",
fheight: null,
style: "excalidraw-svg",
});
const div = createDiv("", async (el) => {
el.appendChild(img);
el.setAttribute("src", file.path);
el.onClickEvent((ev) => {
ev.stopImmediatePropagation();
const src = el.getAttribute("src");
if (src) {
this.openDrawing(
this.app.vault.getAbstractFileByPath(src) as TFile,
ev[CTRL_OR_CMD],
);
} //.ctrlKey||ev.metaKey);
});
});
node.appendChild(div);
});
this.observer = observer;
this.observer.observe(document, { childList: true, subtree: true });
}
@@ -1007,21 +685,27 @@ export default class ExcalidrawPlugin extends Plugin {
if (!activeView) {
return;
}
const filename = `${activeView.file.basename}_${window
const prefix = this.settings.drawingEmbedPrefixWithFilename
? `${activeView.file.basename}_`
: "";
const date = window
.moment()
.format(this.settings.drawingFilenameDateTime)}${
this.settings.compatibilityMode ? ".excalidraw" : ".excalidraw.md"
}`;
const fFp = await getAttachmentsFolderAndFilePath(
this.app,
activeView.file.path,
filename,
);
const file = await this.createDrawing(
filename,
fFp.folder === "" ? null : fFp.folder,
);
await this.embedDrawing(fFp.filepath);
.format(this.settings.drawingFilenameDateTime);
const extension = this.settings.compatibilityMode
? ".excalidraw"
: ".excalidraw.md";
const filename = prefix + date + extension;
const folder = this.settings.embedUseExcalidrawFolder
? null
: (
await getAttachmentsFolderAndFilePath(
this.app,
activeView.file.path,
filename,
)
).folder;
const file = await this.createDrawing(filename, folder);
await this.embedDrawing(file.path);
this.openDrawing(file, inNewPane);
};
@@ -1461,10 +1145,31 @@ export default class ExcalidrawPlugin extends Plugin {
);
}
public ctrlKeyDown: boolean;
public shiftKeyDown: boolean;
public altKeyDown: boolean;
public onKeyUp: any;
public onKeyDown: any;
private popScope: Function = null;
private registerEventListeners() {
const self = this;
this.app.workspace.onLayoutReady(async () => {
self.onKeyUp = (e: KeyboardEvent) => {
self.ctrlKeyDown = e[CTRL_OR_CMD];
self.shiftKeyDown = e.shiftKey;
self.altKeyDown = e.altKey;
};
self.onKeyDown = (e: KeyboardEvent) => {
this.ctrlKeyDown = e[CTRL_OR_CMD];
this.shiftKeyDown = e.shiftKey;
this.altKeyDown = e.altKey;
};
window.addEventListener("keydown", self.onKeyDown, false);
window.addEventListener("keyup", self.onKeyUp, false);
//watch filename change to rename .svg, .png; to sync to .md; to update links
const renameEventHandler = async (
file: TAbstractFile,
@@ -1637,6 +1342,9 @@ export default class ExcalidrawPlugin extends Plugin {
}
onunload() {
window.removeEventListener("keydown", this.onKeyDown, false);
window.removeEventListener("keyup", this.onKeyUp, false);
destroyExcalidrawAutomate();
if (this.popScope) {
this.popScope();

View File

@@ -4,6 +4,7 @@ import {
normalizePath,
PluginSettingTab,
Setting,
TFile,
} from "obsidian";
import { VIEW_TYPE_EXCALIDRAW } from "./constants";
import ExcalidrawView from "./ExcalidrawView";
@@ -12,9 +13,11 @@ import type ExcalidrawPlugin from "./main";
export interface ExcalidrawSettings {
folder: string;
embedUseExcalidrawFolder: boolean;
templateFilePath: string;
scriptFolderPath: string;
drawingFilenamePrefix: string;
drawingEmbedPrefixWithFilename: boolean;
drawingFilenameDateTime: string;
displaySVGInPreview: boolean;
previewMatchObsidianTheme: boolean;
@@ -45,6 +48,9 @@ export interface ExcalidrawSettings {
compatibilityMode: boolean;
experimentalFileType: boolean;
experimentalFileTag: string;
experimentalLivePreview: boolean;
experimentalEnableFourthFont: boolean;
experimantalFourthFont: string;
loadCount: number; //version 1.2 migration counter
drawingOpenCount: number;
library: string;
@@ -58,13 +64,16 @@ export interface ExcalidrawSettings {
mdFont: string;
mdFontColor: string;
mdCSS: string;
scriptEngineSettings: {};
}
export const DEFAULT_SETTINGS: ExcalidrawSettings = {
folder: "Excalidraw",
embedUseExcalidrawFolder: false,
templateFilePath: "Excalidraw/Template.excalidraw",
scriptFolderPath: "Excalidraw/Scripts",
drawingFilenamePrefix: "Drawing ",
drawingEmbedPrefixWithFilename: true,
drawingFilenameDateTime: "YYYY-MM-DD HH.mm.ss",
displaySVGInPreview: true,
previewMatchObsidianTheme: false,
@@ -94,6 +103,9 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
syncExcalidraw: false,
experimentalFileType: false,
experimentalFileTag: "✏️",
experimentalLivePreview: true,
experimentalEnableFourthFont: false,
experimantalFourthFont: "Virgil",
compatibilityMode: false,
loadCount: 0,
drawingOpenCount: 0,
@@ -113,13 +125,14 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
mdFont: "Virgil",
mdFontColor: "Black",
mdCSS: "",
scriptEngineSettings: {},
};
export class ExcalidrawSettingTab extends PluginSettingTab {
plugin: ExcalidrawPlugin;
private requestEmbedUpdate: boolean = false;
private requestReloadDrawings: boolean = false;
private applyDebounceTimer: number = 0;
//private applyDebounceTimer: number = 0;
constructor(app: App, plugin: ExcalidrawPlugin) {
super(app, plugin);
@@ -127,17 +140,22 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}
applySettingsUpdate(requestReloadDrawings: boolean = false) {
clearTimeout(this.applyDebounceTimer);
const plugin = this.plugin;
this.applyDebounceTimer = window.setTimeout(() => {
plugin.saveSettings();
}, 100);
if (requestReloadDrawings) {
this.requestReloadDrawings = true;
}
}
async hide() {
this.plugin.settings.scriptFolderPath = normalizePath(
this.plugin.settings.scriptFolderPath,
);
if (
this.plugin.settings.scriptFolderPath === "/" ||
this.plugin.settings.scriptFolderPath === ""
) {
this.plugin.settings.scriptFolderPath = "Excalidraw/Scripts";
}
this.plugin.saveSettings();
if (this.requestReloadDrawings) {
const exs =
this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
@@ -153,13 +171,6 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
if (this.requestEmbedUpdate) {
this.plugin.triggerEmbedUpdates();
}
if (
this.plugin.settings.scriptFolderPath === "/" ||
this.plugin.settings.scriptFolderPath === ""
) {
this.plugin.settings.scriptFolderPath = "Excalidraw/Scripts";
this.plugin.saveSettings();
}
this.plugin.scriptEngine.updateScriptPath();
}
@@ -195,6 +206,18 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}),
);
new Setting(containerEl)
.setName(t("FOLDER_EMBED_NAME"))
.setDesc(t("FOLDER_EMBED_DESC"))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.embedUseExcalidrawFolder)
.onChange(async (value) => {
this.plugin.settings.embedUseExcalidrawFolder = value;
this.applySettingsUpdate();
}),
);
new Setting(containerEl)
.setName(t("TEMPLATE_NAME"))
.setDesc(t("TEMPLATE_DESC"))
@@ -216,7 +239,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setPlaceholder("Excalidraw/Scripts")
.setValue(this.plugin.settings.scriptFolderPath)
.onChange(async (value) => {
this.plugin.settings.scriptFolderPath = normalizePath(value);
this.plugin.settings.scriptFolderPath = value;
this.applySettingsUpdate();
}),
);
@@ -255,6 +278,18 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}),
);
new Setting(containerEl)
.setName(t("FILENAME_PREFIX_EMBED_NAME"))
.setDesc(t("FILENAME_PREFIX_EMBED_DESC"))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.drawingEmbedPrefixWithFilename)
.onChange(async (value) => {
this.plugin.settings.drawingEmbedPrefixWithFilename = value;
this.applySettingsUpdate();
}),
);
new Setting(containerEl)
.setName(t("FILENAME_DATE_NAME"))
.setDesc(t("FILENAME_DATE_DESC"))
@@ -539,16 +574,21 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName(t("MD_DEFAULT_FONT_NAME"))
.setDesc(t("MD_DEFAULT_FONT_DESC"))
.addText((text) =>
text
.setPlaceholder("Virgil|Cascadia|Filename")
.setValue(this.plugin.settings.mdFont)
.onChange((value) => {
this.requestReloadDrawings = true;
this.plugin.settings.mdFont = value;
this.applySettingsUpdate(true);
}),
);
.addDropdown(async (d: DropdownComponent) => {
d.addOption("Virgil", "Virgil");
d.addOption("Cascadia", "Cascadia");
this.app.vault
.getFiles()
.filter((f) => ["ttf", "woff", "woff2"].contains(f.extension))
.forEach((f: TFile) => {
d.addOption(f.path, f.name);
});
d.setValue(this.plugin.settings.mdFont).onChange((value) => {
this.requestReloadDrawings = true;
this.plugin.settings.mdFont = value;
this.applySettingsUpdate(true);
});
});
new Setting(containerEl)
.setName(t("MD_DEFAULT_COLOR_NAME"))
@@ -826,5 +866,226 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
this.applySettingsUpdate();
}),
);
new Setting(containerEl)
.setName(t("LIVEPREVIEW_NAME"))
.setDesc(t("LIVEPREVIEW_DESC"))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.experimentalLivePreview)
.onChange(async (value) => {
this.plugin.settings.experimentalLivePreview = value;
this.applySettingsUpdate();
}),
);
new Setting(containerEl)
.setName(t("ENABLE_FOURTH_FONT_NAME"))
.setDesc(t("ENABLE_FOURTH_FONT_DESC"))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.experimentalEnableFourthFont)
.onChange(async (value) => {
this.requestReloadDrawings = true;
this.plugin.settings.experimentalEnableFourthFont = value;
this.applySettingsUpdate();
}),
);
new Setting(containerEl)
.setName(t("FOURTH_FONT_NAME"))
.setDesc(t("FOURTH_FONT_DESC"))
.addDropdown(async (d: DropdownComponent) => {
d.addOption("Virgil", "Virgil");
this.app.vault
.getFiles()
.filter((f) => ["ttf", "woff", "woff2"].contains(f.extension))
.forEach((f: TFile) => {
d.addOption(f.path, f.name);
});
d.setValue(this.plugin.settings.experimantalFourthFont).onChange(
(value) => {
this.requestReloadDrawings = true;
this.plugin.settings.experimantalFourthFont = value;
this.applySettingsUpdate(true);
this.plugin.initializeFourthFont();
},
);
});
if (Object.keys(this.plugin.settings.scriptEngineSettings).length > 0) {
const getValue = (scriptName: string, variableName: string): any => {
const variable =
//@ts-ignore
this.plugin.settings.scriptEngineSettings[scriptName][variableName];
switch (typeof variable) {
case "object":
return variable.value;
default:
return variable;
}
};
const setValue = (
scriptName: string,
variableName: string,
value: any,
) => {
switch (
//@ts-ignore
typeof this.plugin.settings.scriptEngineSettings[scriptName][
variableName
]
) {
case "object":
//@ts-ignore
this.plugin.settings.scriptEngineSettings[scriptName][
variableName
].value = value;
break;
default:
//@ts-ignore
this.plugin.settings.scriptEngineSettings[scriptName][
variableName
] = value;
}
};
const addBooleanSetting = (
scriptName: string,
variableName: string,
description?: string,
) => {
new Setting(containerEl)
.setName(variableName)
.setDesc(description ?? "")
.addToggle((toggle) =>
toggle
.setValue(getValue(scriptName, variableName))
.onChange(async (value) => {
setValue(scriptName, variableName, value);
this.applySettingsUpdate();
}),
);
};
const addStringSetting = (
scriptName: string,
variableName: string,
description?: string,
valueset?: any,
) => {
if (
valueset &&
Object.prototype.toString.call(valueset) === "[object Array]" &&
valueset.length > 0
) {
new Setting(containerEl)
.setName(variableName)
.setDesc(description ?? "")
.addDropdown((dropdown) => {
valueset.forEach((val: any) =>
dropdown.addOption(val.toString(), val.toString()),
);
dropdown
.setValue(getValue(scriptName, variableName))
.onChange(async (value) => {
setValue(scriptName, variableName, value);
this.applySettingsUpdate();
});
});
} else {
new Setting(containerEl)
.setName(variableName)
.setDesc(description ?? "")
.addText((text) =>
text
.setValue(getValue(scriptName, variableName))
.onChange(async (value) => {
setValue(scriptName, variableName, value);
this.applySettingsUpdate();
}),
);
}
};
const addNumberSetting = (
scriptName: string,
variableName: string,
description?: string,
) => {
new Setting(containerEl)
.setName(variableName)
.setDesc(description ?? "")
.addText((text) =>
text
.setPlaceholder("Enter a number")
.setValue(getValue(scriptName, variableName).toString())
.onChange(async (value) => {
const numVal = parseFloat(value);
if (isNaN(numVal) && value !== "") {
text.setValue(getValue(scriptName, variableName).toString());
return;
}
setValue(scriptName, variableName, isNaN(numVal) ? 0 : numVal);
this.applySettingsUpdate();
}),
);
};
this.containerEl.createEl("h1", { text: t("SCRIPT_SETTINGS_HEAD") });
Object.keys(this.plugin.settings.scriptEngineSettings).forEach(
(scriptName: string) => {
const settings =
//@ts-ignore
this.plugin.settings.scriptEngineSettings[scriptName];
const values = Object.values(settings);
if (
values.length === 0 ||
(values.length > 0 &&
values
.map((val: any): number => (val.hidden ? 0 : 1))
.reduce((prev, cur) => prev + cur) === 0)
) {
return;
}
this.containerEl.createEl("h3", { text: scriptName });
Object.keys(settings).forEach((variableName) => {
const variable = settings[variableName];
const item = variable.value ?? variable;
switch (typeof item) {
case "boolean":
if (!variable.hidden) {
addBooleanSetting(
scriptName,
variableName,
variable.description,
);
}
break;
case "string":
if (!variable.hidden) {
addStringSetting(
scriptName,
variableName,
variable.description,
variable.valueset,
);
}
break;
case "number":
if (!variable.hidden) {
addNumberSetting(
scriptName,
variableName,
variable.description,
);
}
break;
}
});
},
);
}
}
}

View File

@@ -35,6 +35,10 @@ img.excalidraw-svg-right {
float: right;
}
.excalidraw-svg-center {
text-align: center;
}
img.excalidraw-svg-left {
float: left;
}
@@ -125,6 +129,7 @@ li[data-testid] {
.modal-content.excalidraw-scriptengine-install {
max-width: 130ch;
user-select: text;
}
.excalidraw-scriptengine-install .modal {

View File

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

12583
yarn.lock

File diff suppressed because it is too large Load Diff