mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
83 Commits
1.6.25-bet
...
1.7.12
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38add10053 | ||
|
|
21dc29d1d1 | ||
|
|
e7444f5d8a | ||
|
|
cc9d7828c7 | ||
|
|
b67d70c519 | ||
|
|
5c861acea6 | ||
|
|
582c3dbf25 | ||
|
|
e38e21f3ee | ||
|
|
d946b6fbfa | ||
|
|
e6fd56d9ee | ||
|
|
de44b01e0c | ||
|
|
562f3d3f5b | ||
|
|
c0c2b0a014 | ||
|
|
16ff10b894 | ||
|
|
b4ddb7a8c9 | ||
|
|
43176e59c3 | ||
|
|
9db43f6d4a | ||
|
|
c18262180f | ||
|
|
509b60cb69 | ||
|
|
6217a0601c | ||
|
|
be5d35e811 | ||
|
|
e759010ae0 | ||
|
|
8ff0d187df | ||
|
|
358ab82b85 | ||
|
|
92cda3b4f9 | ||
|
|
9fafbd82c2 | ||
|
|
37dadda342 | ||
|
|
b041088b59 | ||
|
|
1a04cd88bd | ||
|
|
d0e504c369 | ||
|
|
83005da1bb | ||
|
|
f574721574 | ||
|
|
a7af6b2421 | ||
|
|
e708be8eda | ||
|
|
ea39b8c6a1 | ||
|
|
e5fb705f0b | ||
|
|
8046b5dc1f | ||
|
|
08e4e1f131 | ||
|
|
108293ae5c | ||
|
|
73528596d2 | ||
|
|
d2284b8d14 | ||
|
|
7cd3ec40c6 | ||
|
|
80cbb41913 | ||
|
|
257f3d17ac | ||
|
|
2e6b76b0f3 | ||
|
|
9030a77914 | ||
|
|
461b472f20 | ||
|
|
6bacae16b2 | ||
|
|
fa0f388e4a | ||
|
|
afc4f614b7 | ||
|
|
9e05493a9d | ||
|
|
a20900749d | ||
|
|
461eeafd80 | ||
|
|
5ff8caa04f | ||
|
|
9051fe2c01 | ||
|
|
018e42b34b | ||
|
|
b29e79f2b4 | ||
|
|
1ce0051d4e | ||
|
|
c44a4a91b3 | ||
|
|
2c5a4c01d2 | ||
|
|
668c4d62e9 | ||
|
|
0ce4f5f16b | ||
|
|
56d0966d4e | ||
|
|
e000c6bf39 | ||
|
|
feb5b576cb | ||
|
|
2bd1f68276 | ||
|
|
22d1a9b90a | ||
|
|
00a6a3dcaf | ||
|
|
82061cd126 | ||
|
|
4bdd095ff0 | ||
|
|
f40ad62291 | ||
|
|
de4fc602ca | ||
|
|
f5faec8ac9 | ||
|
|
8834762004 | ||
|
|
48477c7ee9 | ||
|
|
f0f65fb9a3 | ||
|
|
26e12e8cec | ||
|
|
fa2fe5e462 | ||
|
|
83a70828ae | ||
|
|
b2e246bdf4 | ||
|
|
fd0f39c214 | ||
|
|
2862fbc983 | ||
|
|
5f42ed8f8d |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -12,3 +12,7 @@ main.js
|
||||
stats.html
|
||||
hot-reload.bat
|
||||
data.json
|
||||
lib
|
||||
|
||||
#VSCode
|
||||
.vscode
|
||||
@@ -6,7 +6,7 @@ With a little work, using Excalidraw Automate you can generate simple mindmaps,
|
||||
|
||||
You can access Excalidraw Automate via the ExcalidrawAutomate object. I recommend starting your Automate scripts with the following code.
|
||||
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
const ea = ExcalidrawAutomate;
|
||||
ea.reset();
|
||||
@@ -27,7 +27,7 @@ You can change styling between adding different elements. My logic for separatin
|
||||
#### Create a new drawing with custom name, in a custom folder, using a template
|
||||
This simple script gives you significant additional flexibility over Excalidraw Plugin settings to name your drawings, place them into folders, and to apply templates.
|
||||
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const ea = ExcalidrawAutomate;
|
||||
@@ -42,7 +42,7 @@ This simple script gives you significant additional flexibility over Excalidraw
|
||||
```
|
||||
|
||||
#### Create a simple drawing
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const ea = ExcalidrawAutomate;
|
||||
@@ -63,7 +63,7 @@ The script will generate the following drawing:
|
||||
|
||||
## Attributes and functions at a glance
|
||||
Here's the interface implemented by ExcalidrawAutomate:
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
ExcalidrawAutomate: {
|
||||
style: {
|
||||
@@ -324,7 +324,7 @@ Returns a blob containing a PNG image of the generated drawing.
|
||||
### Insert new drawing into currently edited document
|
||||
This template will prompt you for the title of the drawing. It will create a new drawing with the provided title, and in the folder of the document you were editing. It will then transclude the new drawing at the cursor location and open the new drawing in a new workspace leaf by splitting the current leaf.
|
||||
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const defaultTitle = tp.date.now("HHmm")+' '+tp.file.title;
|
||||
@@ -345,7 +345,7 @@ This template will prompt you for the title of the drawing. It will create a new
|
||||
```
|
||||
|
||||
### Connect objects
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const ea = ExcalidrawAutomate;
|
||||
@@ -362,7 +362,7 @@ This template will prompt you for the title of the drawing. It will create a new
|
||||
### Using a template
|
||||
This example is similar to the first one, but rotated 90°, and using a template, plus specifying a filename and folder to save the drawing, and opening the new drawing in a new pane.
|
||||
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const ea = ExcalidrawAutomate;
|
||||
@@ -402,7 +402,7 @@ Example input:
|
||||
|
||||
The script:
|
||||
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const IDX = Object.freeze({"depth":0, "text":1, "parent":2, "size":3, "children": 4, "objectId":5});
|
||||
|
||||
44
README.md
44
README.md
@@ -17,14 +17,18 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
|
||||
|[](https://youtu.be/eKFmrSQhFA4)|[](https://youtu.be/qbPIAZguJeo)|[](https://youtu.be/2Y8OhkGiTHg)|
|
||||
|[](https://youtu.be/2v9TZmQNO8c)|[](https://youtu.be/xHPGWR3m0c8)|[](https://youtu.be/gMIKXyhS-dM)|
|
||||
|[](https://youtu.be/Etskjw7a5zo)|[](https://youtu.be/4N6efq1DtH0)|[](https://youtu.be/U2LkBRBk4LY)|
|
||||
| [](https://youtu.be/qiKuqMcNWgU)| | |
|
||||
|
||||
|
||||
|
||||
|
||||
# Key features
|
||||
- The plugin aims to integrate Excalidraw seamlessly into Obsidian including Command Palette actions, File Explorer features, Option Menu commands, and the Ribbon Button.
|
||||
- CTRL/CMD+Click on the ribbon button, or in the file explorer to create / open drawings in a new pane.
|
||||
- <kbd>CTRL/CMD+Click</kbd> on the ribbon button, or in the file explorer to create / open drawings in a new pane.
|
||||
- Settings will allow you to customize Excalidraw to your needs:
|
||||
- Default folder for new drawings and define custom filename pattern for new drawings.
|
||||
- Template for new drawings. The template will restore stroke properties. This means you can set up defaults in your template for stroke color, stroke width, opacity, font family, font size, fill style, stroke style, etc. This also applies to ExcalidrawAutomate.
|
||||
- If portability is important to you: Auto-export SVG and/or PNG files including keep-in-sync feature so you can embed SVG/PNG into your documents instead of embedding excalidraw files.
|
||||
- If portability is important to you: Auto-export SVG and/or PNG files including keep-in-sync feature so you can embed SVG/PNG into your documents instead of embedding excalidraw files. You can override export settings for an individual file by adding the `excalidraw-autoexport` frontmatter key. Valid values for this key are `none`, `both`, `png` and `svg`.
|
||||
- Specify the default width of embedded drawings.
|
||||
- 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.
|
||||
@@ -38,17 +42,17 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
|
||||
- `![[myfile#section]]` also works, this will transclude the section
|
||||
- you can also specify word wrapping for transcluded text by adding the max character count in curly brackets right after the transclusion e.g. `![[myfile#^blockref]]{40}` will wrap text at 40 characters.
|
||||
- For convenience you can also use the command palette to insert links into drawings
|
||||
- CTRL/CMD + hover to bring up the Obsidian quick preview for the link. (On Mac it is CTRL+CMD+hover).
|
||||
- CTRL/CMD + CLICK a text element to open it as a link.
|
||||
- CTRL/CMD + ALT + CLICK to create the file (if it does not yet exist) and open it
|
||||
- CTRL/CMD + SHIFT + CLICK to open the file in a new pane
|
||||
- CTRL/CMD + ALT + SHIFT + CLICK to create the file (if it does not yet exist) and open it in a new pane
|
||||
- <kbd>CTRL/CMD + hover</kbd> to bring up the Obsidian quick preview for the link. (On Mac it is <kbd>CTRL+CMD+hover</kbd>).
|
||||
- <kbd>CTRL/CMD + CLICK</kbd> a text element to open it as a link.
|
||||
- <kbd>CTRL/CMD + ALT + CLICK</kbd> to create the file (if it does not yet exist) and open it
|
||||
- <kbd>CTRL/CMD + SHIFT + CLICK</kbd> to open the file in a new pane
|
||||
- <kbd>CTRL/CMD + ALT + SHIFT + CLICK</kbd> to create the file (if it does not yet exist) and open it in a new pane
|
||||
- Using the block reference you can also reference & transclude text that appears on drawings, in other documents
|
||||
- Insert LaTeX formulas using the Command Palette action "Insert LaTeX formula". You can edit formulas either in Markdown view, or by CTRL/CMD + Click on the formula.
|
||||
- Insert LaTeX formulas using the Command Palette action "Insert LaTeX formula". You can edit formulas either in Markdown view, or by <kbd>CTRL/CMD + Click</kbd> on the formula.
|
||||
- Drag & Drop support
|
||||
- You can drag files from the Obsidian file explorer and they will become links to those files in Excalidraw.
|
||||
- Dragging image files (PNG, SVG, JPG, Excalidraw) from obsidian files explorer while pressing the CTRL/CMD button will embed the image into your drawing.
|
||||
- You can drag and drop images from outside obsidian onto Excalidraw. These images will be embedded into your drawing and saved to Obsidian.
|
||||
- Dragging image files (PNG, SVG, JPG, Excalidraw) from Obsidian's file explorer while pressing the <kbd>CTRL/CMD</kbd> button will embed the image into your drawing.
|
||||
- You can drag and drop images from outside Obsidian onto Excalidraw. These images will be embedded into your drawing and saved to Obsidian.
|
||||
- You can drag and drop text from Markdown views onto Excalidraw.
|
||||
- You can drag and drop web addresses from your browser and they will become links.
|
||||
- Image support
|
||||
@@ -71,30 +75,22 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
|
||||
- `excalidraw-export-svgpadding`: This only affects export to SVG. Specify the export padding for the image
|
||||
- `excalidraw-export-pngscale`: This only affects export to PNG. Specify the export scale for the image. The typical range is between 0.5 and 5, but you can experiment with other values as well.
|
||||
- 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.
|
||||
- Drag from the desired file from the Obsidian file explorer and hold down <kbd>CTRL/CMD</kbd> while dropping the file onto the canvas.
|
||||
- Use the command palette action: `Insert markdown file from vault`
|
||||
- Use custom woff, woff2 or TTF font to display the document, you can set the default font to use under Excalidraw Settings.
|
||||
- You can set a custom css for rendering the snapshot image of your markdown document. Only operating system standard fonts are supported as the font-family ([Win10](https://docs.microsoft.com/en-us/typography/fonts/windows_10_font_list), [Mac & iOS](https://developer.apple.com/fonts/system-fonts/)), plus you can set one additional custom font using the setting explained above. (for a demonstration watch this [video](https://youtu.be/K6qZkTz8GHs) and check out this [sample css](https://github.com/zsviczian/obsidian-excalidraw-plugin/discussions/281)).
|
||||
- To help with styling you can observe the SVG snapshot of the markdown document created by Excalidraw. Open Obsidian Developer Console (CTRL+Shift+i) and execute the following command: `ExcalidrawAutomate.mostRecentMarkdownSVG`
|
||||
- To help with styling you can observe the SVG snapshot of the markdown document created by Excalidraw. Open Obsidian Developer Console (<kbd>CTRL+Shift+i</kbd>) and execute the following command: `ExcalidrawAutomate.mostRecentMarkdownSVG`
|
||||
- You can control appearance of the embedded markdown file on a file by file bases by adding the following front matter keys to your markdown document:
|
||||
- `excalidraw-font: Virgil|Cascadia|font_file_name.extension`
|
||||
- `excalidraw-font-color: css-color-name|#HEXcolor|any-other-html-standard-format`, you can find css color names [here](https://www.w3schools.com/colors/colors_names.asp).
|
||||
- `excalidraw-border-color: css-color-name|#HEXcolor|any-other-html-standard-format`
|
||||
- `excalidraw-css: "css-filename|css snippet"`
|
||||
- Switch to markdown view or use CTRL/CMD+ALT/OPT click on the image to edit properties of the embed: `[[filename#^blockref|WIDTHxMAXHEIGHT]]`
|
||||
- Switch to markdown view or use <kbd>CTRL/CMD+ALT/OPT</kbd> click on the image to edit properties of the embed: `[[filename#^blockref|WIDTHxMAXHEIGHT]]`
|
||||
- Includes full [QuickAdd](https://github.com/chhoumann/quickadd), [Templater](https://silentvoid13.github.io/Templater/) and [Dataview](https://blacksmithgu.github.io/obsidian-dataview/docs/api/intro/) support through ExcalidrawAutomate. Check out the [detailed help + examples](https://zsviczian.github.io/obsidian-excalidraw-plugin/). I also have a [YouTube ExcalidrawAutomate Playlist](https://www.youtube.com/playlist?list=PL6mqgtMZ4NP1IR4nXxSlMA4PA5E-qpyHZ) with lots of examples.
|
||||
- Since 1.5.0 you can easily execute ExcalidrawAutomate macros and assign command palette shortcuts to them, using the ScriptEngine. You will find an intro video and a growing library of ready to install scripts [here](https://github.com/zsviczian/obsidian-excalidraw-plugin/tree/master/ea-scripts).
|
||||
- REQUIRES AN OBSIDIAN SYNC SUBSCRIPTION: Full drawing file history and synchronization between devices
|
||||
- Multilanguage support: if you'd like to help out by translating the plugin, please get in contact with me.
|
||||
|
||||
# Known issues
|
||||
- Mobile support
|
||||
- Partially mitigated in 1.0.10 by the introduction of autosave: Your drawing will not be saved when you terminate the mobile app by closing the Obsidian task.
|
||||
- Text elements "jumps off screen" when editing, if drawing is zoomed in and text element does not fit the visible screen area. I am working on a resolution.
|
||||
|
||||
# Tips and tricks
|
||||
- [Ozan's Image in Editor Plugin](https://github.com/ozntel/oz-image-in-editor-obsidian). In a nice collaboration with Ozan, his Image-in-Editor plugin now supports Excalidraw. I recommend installing his plugin to display drawings also in Edit mode.
|
||||
|
||||
# Feedback, questions, ideas, problems
|
||||
Join the conversation about the Excalidraw plugin on [forum.obsidian.md](https://forum.obsidian.md/t/excalidraw-full-featured-sketching-plugin-in-obsidian)
|
||||
|
||||
@@ -108,3 +104,9 @@ Please also help spread the word by sharing about the Obsidian Excalidraw Plugin
|
||||
You can find me on Twitter [@zsviczian](https://twitter.com/zsviczian), and on my blog [zsolt.blog](https://zsolt.blog).
|
||||
|
||||
[<img style="float:left" src="https://user-images.githubusercontent.com/14358394/115450238-f39e8100-a21b-11eb-89d0-fa4b82cdbce8.png" width="200">](https://ko-fi.com/zsolt)
|
||||
|
||||
# Friends of Excalidraw
|
||||
If you enjoy Excalidraw, consider giving [ExcaliBrain](https://github.com/zsviczian/excalibrain) a try (also available via Obsidian Community Plugins).
|
||||
|
||||
[](https://youtu.be/gOkniMkDPyM)
|
||||
|
||||
|
||||
36
dev.postprocess.config.js
Normal file
36
dev.postprocess.config.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import fs from'fs';
|
||||
import LZString from 'lz-string';
|
||||
|
||||
const excalidraw_pkg = isProd
|
||||
? fs.readFileSync("./node_modules/@zsviczian/excalidraw/dist/excalidraw.production.min.js", "utf8")
|
||||
: fs.readFileSync("./node_modules/@zsviczian/excalidraw/dist/excalidraw.development.js", "utf8");
|
||||
const react_pkg = isProd
|
||||
? fs.readFileSync("./node_modules/react/umd/react.production.min.js", "utf8")
|
||||
: fs.readFileSync("./node_modules/react/umd/react.development.js", "utf8");
|
||||
const reactdom_pkg = isProd
|
||||
? fs.readFileSync("./node_modules/react-dom/umd/react-dom.production.min.js", "utf8")
|
||||
: fs.readFileSync("./node_modules/react-dom/umd/react-dom.development.js", "utf8");
|
||||
const lzstring_pkg = fs.readFileSync("./node_modules/lz-string/libs/lz-string.min.js", "utf8")
|
||||
|
||||
const packageString = lzstring_pkg+'const EXCALIDRAW_PACKAGES = "' + LZString.compressToBase64(react_pkg + reactdom_pkg + excalidraw_pkg) +'";var ExcalidrawPackageLoader=(d=document)=>{const excalidraw_id = "excalidraw-script";if(!d.getElementById(excalidraw_id)){const script=d.createElement("script");script.type="text/javascript";script.id=excalidraw_id;script.text=LZString.decompressFromBase64(EXCALIDRAW_PACKAGES);d.body.appendChild(script);}};ExcalidrawPackageLoader();';
|
||||
|
||||
const mainjs = fs.readFileSync("main.js", "utf8")
|
||||
|
||||
|
||||
fs.writeFileSync(
|
||||
"main2.js",
|
||||
mainjs
|
||||
.replace('(require("react"));','')
|
||||
.replace('"use strict";','"use strict";' + packageString),
|
||||
{
|
||||
encoding: "utf8",
|
||||
flag: "w",
|
||||
mode: 0o666
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
let config = {
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -2,7 +2,7 @@
|
||||
## Introduction to the API
|
||||
You can access Excalidraw Automate via the ExcalidrawAutomate object. I recommend starting Templater, DataView and QuickAdd scripts with the following code:
|
||||
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
const ea = ExcalidrawAutomate;
|
||||
ea.reset();
|
||||
@@ -25,7 +25,7 @@ You can change the styling between adding different elements. My logic for separ
|
||||
#### Create a new drawing with custom name, in a custom folder, using a template
|
||||
This simple script gives you significant additional flexibility over Excalidraw Plugin settings to name your drawings, place them into folders, and to apply templates.
|
||||
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const ea = ExcalidrawAutomate;
|
||||
@@ -40,7 +40,7 @@ This simple script gives you significant additional flexibility over Excalidraw
|
||||
```
|
||||
|
||||
#### Create a simple drawing
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const ea = ExcalidrawAutomate;
|
||||
@@ -62,7 +62,7 @@ The script will generate the following drawing:
|
||||
#### Add a TextElement in a box to an open Excalidraw View.
|
||||
Position the new element under the currently selected element, with an arrow from the selected element to the added text.
|
||||
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const ea = ExcalidrawAutomate;
|
||||
|
||||
@@ -143,7 +143,7 @@ getViewSelectedElement():ExcalidrawElement
|
||||
|
||||
You first need to set the view calling `setView()`.
|
||||
|
||||
If an element is selected in the targetView the function returns the selected element. If multiple elements are selected, either by SHIFT+Clicking to select multiple elements, or by selecting a group, the first of the elements will be selected. If you want to specify which element to select from a group, double click the desired element in the group.
|
||||
If an element is selected in the targetView the function returns the selected element. If multiple elements are selected, either by <kbd>SHIFT+Clicking</kbd> to select multiple elements, or by selecting a group, the first of the elements will be selected. If you want to specify which element to select from a group, double click the desired element in the group.
|
||||
|
||||
This function is helpful if you want to add a new element in relation to an existing element in your drawing.
|
||||
|
||||
@@ -218,4 +218,4 @@ ea.onDropHook = (data) => {
|
||||
console.log(data);
|
||||
return false;
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Applying an Excalidraw Template to a New Drawing
|
||||
This example is similar to the one in the introduction, only rotated 90°, and using a template, plus specifying a filename and folder to save the drawing, and opening the new drawing in a new pane.
|
||||
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const ea = ExcalidrawAutomate;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Connect Objects
|
||||
This [Templater](https://github.com/SilentVoid13/Templater) template demonstrates how to connect two objects using ExcalidrawAutomate.
|
||||
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const ea = ExcalidrawAutomate;
|
||||
|
||||
@@ -19,7 +19,7 @@ The input file is `Demo.md` with the following contents:
|
||||
|
||||
### dataviewjs script
|
||||
The `dataviewjs` script looks like this:
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
function crawl(subtasks) {
|
||||
let size = subtasks.length > 0 ? 0 : 1; //if no children then a leaf with size 1
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
## Insert new drawing into currently edited document
|
||||
This [Templater](https://github.com/SilentVoid13/Templater) template will prompt you for the title of the drawing. It will create a new drawing with the provided title, and in the folder of the document you were editing. It will then transclude the new drawing at the cursor location and open the new drawing in a new workspace leaf by splitting the current leaf.
|
||||
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const defaultTitle = tp.date.now("HHmm")+' '+tp.file.title;
|
||||
|
||||
@@ -22,7 +22,7 @@ Example input:
|
||||
```
|
||||
|
||||
### Templater script
|
||||
*Use CTRL+Shift+V to paste code into Obsidian!*
|
||||
*Use <kbd>CTRL+Shift+V</kbd> to paste code into Obsidian!*
|
||||
```javascript
|
||||
<%*
|
||||
const IDX = Object.freeze({"depth":0, "text":1, "parent":2, "size":3, "children": 4, "objectId":5});
|
||||
|
||||
@@ -11,8 +11,6 @@ if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.6.1")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const BLANK_DRAWING = ["---","","excalidraw-plugin: parsed","","---","==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠==","","","%%","# Drawing","\x60\x60\x60json",'{"type":"excalidraw","version":2,"source":"https://excalidraw.com","elements":[],"appState":{"gridSize":null,"viewBackgroundColor":"#ffffff"}}',"\x60\x60\x60","%%"].join("\n");
|
||||
|
||||
settings = ea.getScriptSettings();
|
||||
|
||||
if(!settings["Open link in active pane"]) {
|
||||
@@ -60,8 +58,9 @@ const filepath = activeFile.path.replace(activeFile.name,`${filename}.md`);
|
||||
|
||||
const file = await app.fileManager.createNewMarkdownFileFromLinktext(filepath);
|
||||
if(file && fileType==="ex") {
|
||||
await app.vault.modify(file,BLANK_DRAWING);
|
||||
await new Promise(r => setTimeout(r, 100)); //wait for metadata cache to update, so file opens as excalidraw
|
||||
const blank = await app.plugins.plugins["obsidian-excalidraw-plugin"].getBlankDrawing();
|
||||
await app.vault.modify(file,blank);
|
||||
await new Promise(r => setTimeout(r, 100)); //wait for metadata cache to update, so file opens as excalidraw
|
||||
}
|
||||
|
||||
const link = `[[${app.metadataCache.fileToLinktext(file,ea.targetView.file.path,true)}]]`;
|
||||
@@ -88,4 +87,4 @@ if(openInCurrentPane) {
|
||||
app.workspace.openLinkText(file.path,ea.targetView.file.path,false);
|
||||
return;
|
||||
}
|
||||
ea.openFileInNewOrAdjacentLeaf(file);
|
||||
ea.openFileInNewOrAdjacentLeaf(file);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
/*
|
||||

|
||||
|
||||
The script allows you to change the shape of selected Rectangles, Diamonds and Ellipses.
|
||||
The script allows you to change the shape and fill style of selected Rectangles, Diamonds, Ellipses, Lines, Arrows and Freedraw.
|
||||
|
||||
```javascript
|
||||
*/
|
||||
const fillStylesDispaly=["Dots (⚠ VERY SLOW performance on large objects!)","Zigzag","Zigzag-line", "Dashed", "Hachure", "Cross-hatch", "Solid"];
|
||||
const fillStyles=["dots","zigzag","zigzag-line", "dashed", "hachure", "cross-hatch", "solid"];
|
||||
const fillShapes=["ellipse","rectangle","diamond", "freedraw", "line"];
|
||||
const boxShapesDispaly=["○ ellipse","□ rectangle","◇ diamond"];
|
||||
const boxShapes=["ellipse","rectangle","diamond"];
|
||||
const lineShapesDispaly=["- line","⭢ arrow"];
|
||||
@@ -14,18 +17,27 @@ let editedElements = [];
|
||||
|
||||
let elements = ea.getViewSelectedElements().filter(el=>boxShapes.contains(el.type));
|
||||
if (elements.length>0) {
|
||||
newShape = await utils.suggester(boxShapesDispaly, boxShapes, "Change shape of 'box' type elements in selection");
|
||||
newShape = await utils.suggester(boxShapesDispaly, boxShapes, "Change shape of 'box' type elements in selection, press ESC to skip");
|
||||
if(newShape) {
|
||||
editedElements = elements;
|
||||
elements.forEach(el=>el.type = newShape);
|
||||
}
|
||||
}
|
||||
|
||||
elements = ea.getViewSelectedElements().filter(el=>fillShapes.contains(el.type));
|
||||
if (elements.length>0) {
|
||||
newFillStyle = await utils.suggester(fillStylesDispaly, fillStyles, "Change the fill style of elements in selection, press ESC to skip");
|
||||
if(newFillStyle) {
|
||||
editedElements = editedElements.concat(elements.filter(e=>!editedElements.some(el=>el.id===e.id)));
|
||||
elements.forEach(el=>el.fillStyle = newFillStyle);
|
||||
}
|
||||
}
|
||||
|
||||
elements = ea.getViewSelectedElements().filter(el=>lineShapes.contains(el.type));
|
||||
if (elements.length>0) {
|
||||
newShape = await utils.suggester(lineShapesDispaly, lineShapes, "Change shape of 'line' type elements in selection");
|
||||
newShape = await utils.suggester(lineShapesDispaly, lineShapes, "Change shape of 'line' type elements in selection, press ESC to skip");
|
||||
if(newShape) {
|
||||
editedElements = editedElements.concat(elements);
|
||||
editedElements = editedElements.concat(elements.filter(e=>!editedElements.some(el=>el.id===e.id)));
|
||||
elements.forEach((el)=>{
|
||||
el.type = newShape;
|
||||
if(newShape === "arrow") {
|
||||
@@ -37,4 +49,4 @@ if (elements.length>0) {
|
||||
|
||||
ea.copyViewElementsToEAforEditing(editedElements);
|
||||
|
||||
ea.addElementsToView(false,false);
|
||||
ea.addElementsToView(false,false);
|
||||
|
||||
@@ -58,9 +58,9 @@ for (var i = 0; i < topGroups.length; i++) {
|
||||
.filter((el) => el.type === "rectangle")
|
||||
.sort((lha, rha) => lha.y - rha.y);
|
||||
|
||||
const groupWith = groupHeights[i].height;
|
||||
if (groupWith < maxGroupHeight) {
|
||||
const distance = maxGroupHeight - groupWith;
|
||||
const groupWidth = groupHeights[i].height;
|
||||
if (groupWidth < maxGroupHeight) {
|
||||
const distance = maxGroupHeight - groupWidth;
|
||||
const perRectDistance = distance / rects.length;
|
||||
for (var j = 0; j < rects.length; j++) {
|
||||
const rect = rects[j];
|
||||
@@ -128,4 +128,4 @@ function recalculateEndPointOfLine(line, el) {
|
||||
if(intersectA.length > 0) {
|
||||
line.points[line.points.length - 1] = [intersectA[0][0] - line.x, intersectA[0][1] - line.y];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
113
ea-scripts/Mindmap connector.md
Normal file
113
ea-scripts/Mindmap connector.md
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
|
||||

|
||||
|
||||

|
||||
This script creates mindmap like lines(only right and down side are available). The line will starts according to the creation time of the elements. So you may need to create the header element first.
|
||||
|
||||
```javascript
|
||||
*/
|
||||
const elements = ea.getViewSelectedElements();
|
||||
ea.copyViewElementsToEAforEditing(elements);
|
||||
groups = ea.getMaximumGroups(elements);
|
||||
|
||||
els=[];
|
||||
elsx=[];
|
||||
elsy=[];
|
||||
for (i = 0, len =groups.length; i < len; i++) {
|
||||
els.push(ea.getLargestElement(groups[i]));
|
||||
elsx.push(ea.getLargestElement(groups[i]).x);
|
||||
elsy.push(ea.getLargestElement(groups[i]).y);
|
||||
}
|
||||
//line style setting
|
||||
ea.style.strokeColor = els[0].strokeColor;
|
||||
ea.style.strokeWidth = els[0].strokeWidth;
|
||||
ea.style.strokeStyle = els[0].strokeStyle;
|
||||
ea.style.strokeSharpness = els[0].strokeSharpness;
|
||||
//all min max x y
|
||||
let maxy = Math.max.apply(null, elsy);
|
||||
let indexmaxy=elsy.indexOf(maxy);
|
||||
let miny = Math.min.apply(null, elsy);
|
||||
let indexminy = elsy.indexOf(miny);
|
||||
let maxx = Math.max.apply(null, elsx);
|
||||
let indexmaxx = elsx.indexOf(maxx);
|
||||
let minx = Math.min.apply(null, elsx);
|
||||
let indexminx = elsx.indexOf(minx);
|
||||
//child max min x y
|
||||
let gmaxy = Math.max.apply(null, elsy.slice(1));
|
||||
let gindexmaxy=elsy.indexOf(gmaxy);
|
||||
let gminy = Math.min.apply(null, elsy.slice(1));
|
||||
let gindexminy = elsy.indexOf(gminy);
|
||||
let gmaxx = Math.max.apply(null, elsx.slice(1));
|
||||
let gindexmaxx = elsx.indexOf(gmaxx);
|
||||
let gminx = Math.min.apply(null, elsx.slice(1));
|
||||
let gindexminx = elsx.indexOf(gminx);
|
||||
let s=0;//Set line direction down as default
|
||||
if (indexminx==0 && els[0].x + els[0].width<=gminx) {
|
||||
s=1;
|
||||
}
|
||||
else if (indexminy == 0) {
|
||||
s=0;
|
||||
}
|
||||
var length_left;
|
||||
if(els[0].x + els[0].width * 2<=gminx){length_left=els[0].x + els[0].width * 1.5;}
|
||||
else {length_left=(els[0].x + els[0].width+gminx)/2;}
|
||||
|
||||
var length_down;
|
||||
if(els[0].y + els[0].height* 2.5<=gminy){length_down=els[0].y + els[0].height * 2;}
|
||||
else {length_down=(els[0].y + els[0].height+gminy)/2;}
|
||||
if(s) {
|
||||
ea.addLine(
|
||||
[[length_left,
|
||||
maxy + els[indexmaxy].height / 2],
|
||||
[length_left,
|
||||
miny + els[indexminy].height / 2]]
|
||||
);
|
||||
for (i = 1, len = groups.length; i < len; i++) {
|
||||
ea.addLine(
|
||||
[[els[i].x,
|
||||
els[i].y + els[i].height/2],
|
||||
[length_left,
|
||||
els[i].y + els[i].height/2]]
|
||||
);
|
||||
}
|
||||
ea.addArrow(
|
||||
[[els[0].x+els[0].width,
|
||||
els[0].y + els[0].height / 2],
|
||||
[length_left,
|
||||
els[0].y + els[0].height / 2]],
|
||||
{
|
||||
startArrowHead: "none",
|
||||
endArrowHead: "dot"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
else {
|
||||
ea.addLine(
|
||||
[[maxx + els[indexmaxx].width / 2,
|
||||
length_down],
|
||||
[minx + els[indexminx].width / 2,
|
||||
length_down]]
|
||||
);
|
||||
for (i = 1, len = groups.length; i < len; i++) {
|
||||
ea.addLine(
|
||||
[[els[i].x + els[i].width / 2,
|
||||
els[i].y],
|
||||
[els[i].x + els[i].width / 2,
|
||||
length_down]]
|
||||
);
|
||||
}
|
||||
ea.addArrow(
|
||||
[[els[0].x + els[0].width / 2,
|
||||
els[0].y + els[0].height],
|
||||
[els[0].x + els[0].width / 2,
|
||||
length_down]],
|
||||
{
|
||||
startArrowHead: "none",
|
||||
endArrowHead: "dot"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await ea.addElementsToView(false,false,true);
|
||||
12
ea-scripts/Mindmap connector.svg
Normal file
12
ea-scripts/Mindmap connector.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="607" height="541" viewBox="0 0 607 541" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M280 43.355V253.355H140V206.687L0 206.691V346.691H140V300.023H280V510.023C280 522.925 290.453 533.355 303.332 533.355H490.002C502.904 533.355 513.334 522.925 513.334 510.023C513.334 497.144 502.904 486.691 490.002 486.691H326.672V300.021H490.002C502.904 300.021 513.334 289.568 513.334 276.689C513.334 263.81 502.904 253.357 490.002 253.357H326.672V66.6869H490.002C502.904 66.6869 513.334 56.2569 513.334 43.3549C513.334 30.4529 502.904 20.0229 490.002 20.0229H303.332C290.453 20.019 280 30.4489 280 43.3509V43.355ZM46.67 300.025V253.357H93.338V300.025H46.67Z" fill="black"/>
|
||||
<rect x="540" y="23" width="39" height="39" fill="#D9D9D9"/>
|
||||
<rect x="503" width="104" height="95" fill="#D9D9D9"/>
|
||||
<rect x="503.5" y="0.5" width="103" height="94" fill="black" stroke="black"/>
|
||||
<rect x="503" y="223" width="104" height="95" fill="black"/>
|
||||
<rect x="503.5" y="446.5" width="103" height="94" fill="black" stroke="black"/>
|
||||
<path d="M532 243H580V291H532V243Z" fill="white"/>
|
||||
<path d="M532 475H580V523H532V475Z" fill="white"/>
|
||||
<path d="M532 243H580V291H532V243Z" fill="white"/>
|
||||
<path d="M532 23H580V71H532V23Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -39,6 +39,7 @@ Open the script you are interested in and save it to your Obsidian Vault includi
|
||||
|[Fixed vertical distance between centers](Fixed%20vertical%20distance%20between%20centers.md)|This script arranges the selected elements vertically with a fixed center spacing.||[@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.||[@1-2-3](https://github.com/1-2-3)|
|
||||
|[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.||[@1-2-3](https://github.com/1-2-3)|
|
||||
|[Mindmap connector](Mindmap%20connector.md)|This script creates mindmap like lines (only right side and down available currently) for selected elements. The line will start according to the creation time of the elements. So you should create the header element first.||[@xllowl](https://github.com/xllowl)|
|
||||
|[Modify background color opacity](Modify%20background%20color%20opacity.md)|This script changes the opacity of the background color of the selected boxes. The default background color in Excalidraw is so dark that the text is hard to read. You can lighten the color a bit by setting transparency. And you can tweak the transparency over and over again until you're happy with it. Although excalidraw has the opacity option in its native property Settings, it also changes the transparency of the border. Use this script to change only the opacity of the background color without affecting the border.||[@1-2-3](https://github.com/1-2-3)|
|
||||
|[Normalize Selected Arrows](Normalize%20Selected%20Arrows.md)|This script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.||[@1-2-3](https://github.com/1-2-3)|
|
||||
|[OCR - Optical Character Recognition](OCR%20-%20Optical%20Character%20Recognition.md)|The script will 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and 2) will add the text to your drawing as a text element.||[@zsviczian](https://github.com/zsviczian)|
|
||||
@@ -55,7 +56,7 @@ Open the script you are interested in and save it to your Obsidian Vault includi
|
||||
|[Set stroke width of selected elements](Set%20Stroke%20Width%20of%20Selected%20Elements.md)|This script will set the stroke width of selected elements. This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Split text by lines](Split%20text%20by%20lines.md)|Split lines of text into separate text elements for easier reorganization||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Set Text Alignment](Set%20Text%20Alignment.md)|Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[TheBrain-navigation](TheBrain-navigation.md)|An Excalidraw based graph user interface for your Vault. Requires the [Breadcrumbs plugin](https://github.com/SkepticMystic/breadcrumbs) to be installed and configured as well. Generates a user interface similar to that of [TheBrain](https://TheBrain.com). Watch this introduction to this script on [YouTube](https://youtu.be/J4T5KHERH_o).||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[TheBrain-navigation](TheBrain-navigation.md)|An Excalidraw based graph user interface for your Vault. Requires the [Dataview plugin](https://github.com/blacksmithgu/obsidian-dataview). Generates a graph view similar to that of [TheBrain](https://TheBrain.com) plex. Watch introduction to this script on [YouTube](https://youtu.be/plYobK-VufM).||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Toggle Fullscreen on Mobile](Toggle%20Fullscreen%20on%20Mobile.md)|Hides Obsidian workspace leaf padding and header (based on option in settings, default is "hide header" = false) which will take Excalidraw to full screen. ⚠ Note that if the header is not visible, it will be very difficult to invoke the command palette to end full screen. Only hide the header if you have a keyboard or you've practiced opening command palette!||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Transfer TextElements to Excalidraw markdown metadata](Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md)|The script will delete the selected text elements from the canvas and will copy the text from these text elements into the Excalidraw markdown file as metadata. This means, that the text will no longer be visible in the drawing, however you will be able to search for the text in Obsidian and find the drawing containing this image.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Zoom to Fit Selected Elements](Zoom%20to%20Fit%20Selected%20Elements.md)|Similar to Excalidraw standard SHIFT+2 feature: Zoom to fit selected elements, but with the ability to zoom to 1000%. Inspiration: [#272](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/272)||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Zoom to Fit Selected Elements](Zoom%20to%20Fit%20Selected%20Elements.md)|Similar to Excalidraw standard <kbd>SHIFT+2</kbd> 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)|
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
/*
|
||||
An Excalidraw based graph user interface for your Vault. Requires the [Dataview plugin](https://github.com/blacksmithgu/obsidian-dataview). Generates a graph view similar to that of [TheBrain](https://TheBrain.com) plex.
|
||||
|
||||
An Excalidraw based graph user interface for your Vault. Requires the [Breadcrumbs plugin](https://github.com/SkepticMystic/breadcrumbs) to be installed and configured as well. Generates a user interface similar to that of [TheBrain](https://TheBrain.com).
|
||||
|
||||
Watch introduction to this script on [YouTube](https://youtu.be/J4T5KHERH_o).
|
||||
Watch introduction to this script on [YouTube](https://youtu.be/plYobK-VufM).
|
||||
|
||||

|
||||
|
||||
```javascript
|
||||
*/
|
||||
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.6.24")) {
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.6.25")) {
|
||||
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!BCAPI) {
|
||||
new Notice("Breadcrumbs API not found! Install and activate the Breadcrumbs plugin",4000);
|
||||
if(!window.DataviewAPI) {
|
||||
new Notice ("Some features will only work if you have the Dataview plugin installed and enabled", 4000);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -25,11 +24,14 @@ const removeEventHandler = () => {
|
||||
app.workspace.off(EVENT,window.brainGraphEventHandler);
|
||||
if(isBoolean(window.excalidrawView?.linksAlwaysOpenInANewPane)) {
|
||||
window.excalidrawView.linksAlwaysOpenInANewPane = false;
|
||||
const ea = ExcalidrawAutomate;
|
||||
ea.setView(window.excalidrawView);
|
||||
if(ea.targetView?.getExcalidrawAPI) {
|
||||
}
|
||||
const ea = ExcalidrawAutomate;
|
||||
ea.setView(window.excalidrawView);
|
||||
if(ea.targetView?.excalidrawAPI) {
|
||||
try {
|
||||
ea.targetView.semaphores.saving = false;
|
||||
ea.getExcalidrawAPI().updateScene({appState:{viewModeEnabled:false}});
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
delete window.excalidrawView;
|
||||
delete window.excalidrawFile;
|
||||
@@ -45,33 +47,35 @@ if(window.brainGraphEventHandler) {
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Settings
|
||||
// Load Settings
|
||||
//-------------------------------------------------------
|
||||
|
||||
let saveSettings = false;
|
||||
settings = ea.getScriptSettings();
|
||||
//set default values on first run
|
||||
if(!settings["Max number of nodes/domain"]) {
|
||||
settings = {
|
||||
"Confirmation prompt at startup": {
|
||||
value: true,
|
||||
description: "Prompt me to confirm starting of the script because it will overwrite the current active drawing. " +
|
||||
description: "Prompt me to confirm starting of the script because " +
|
||||
"it will overwrite the current active drawing. " +
|
||||
"You can disable this warning by turning off this switch"
|
||||
},
|
||||
"Max number of nodes/domain": {
|
||||
value: 40,
|
||||
value: 30,
|
||||
description: "Maximum number of items to show in each domain: parents, children, siblings, jumps."
|
||||
},
|
||||
"Infer non-Breadcrumbs links": {
|
||||
value: true,
|
||||
description: "Links on the page are children, backlinks to the page are parents. Breadcrumbs take priority."
|
||||
description: "Links on the page are children, backlinks to the page are " +
|
||||
"parents. Breadcrumbs take priority. Inferred nodes have a dashed border."
|
||||
},
|
||||
"Hide attachments": {
|
||||
value: true,
|
||||
description: "Hide attachments. Will only have an effect if Infer non-Breadcrumbs links is turned on."
|
||||
},
|
||||
"Font family": {
|
||||
value: "Code",
|
||||
valueset: ["Hand-drawn","Normal","Code","Fourth (custom) Font"]
|
||||
value: "Code",
|
||||
valueset: ["Hand-drawn","Normal","Code","Fourth (custom) Font"]
|
||||
},
|
||||
"Stroke roughness": {
|
||||
value: "Architect",
|
||||
@@ -122,7 +126,7 @@ if(!settings["Max number of nodes/domain"]) {
|
||||
description: "Any legal HTML color (#000000, rgb, color-name, etc.)."
|
||||
},
|
||||
"Central-node background color": {
|
||||
value: "#dfaf16",
|
||||
value: "#C49A13",
|
||||
description: "Any legal HTML color (#000000, rgb, color-name, etc.)."
|
||||
},
|
||||
"Central-node color": {
|
||||
@@ -154,8 +158,52 @@ if(!settings["Max number of nodes/domain"]) {
|
||||
description: "Any legal HTML color (#000000, rgb, color-name, etc.)."
|
||||
}
|
||||
};
|
||||
};
|
||||
ea.setScriptSettings(settings);
|
||||
saveSettings = true;
|
||||
}
|
||||
if(!settings["Display alias if available"]) {
|
||||
settings["Display alias if available"] = {
|
||||
value: true,
|
||||
description: "Displays the page alias instead of the " +
|
||||
"filename if it is specified in the page's front matter. "
|
||||
};
|
||||
saveSettings = true;
|
||||
}
|
||||
if(!settings["Graph settings JSON"]) {
|
||||
settings["Graph settings JSON"] = {
|
||||
height: "450px",
|
||||
value: `{\n "breadcrumbs": {\n "down": ["children", "child"],\n "up": ["parents", "parent"],\n "jump": ["jump", "jumps"]\n },\n "tags": {\n "#excalidraw": {\n "nodeColor": "hsl(59, 80%, 77%)",\n "gateColor": "#fd7e14",\n "borderColor": "black",\n "backgroundColor": "rgba(50,50,50,0.5)",\n "prefix": "🎨 "\n },\n "#dnp": {\n "prefix": "🗓 "\n }\n }\n}`,
|
||||
description: `This may contain two elements:
|
||||
<ol>
|
||||
<li>A specification of your breadcrumbs hierarchy. Note, that if you have the Breadcrumbs plugin installed and enabled then <code>TheBrain-navigation</code> script will take your hierarchy settings from Breadcrumbs. If Breadcrumbs is disabled, this specification will be used.</li>
|
||||
<li>Formatting of nodes based on page tags. You can specify special formatting rules for tags. If multiple tags are present on the page the first matching a specification will be used. You may provide partial specifications as well. e.g. if you only specify <code>prefix</code>, the other attributes will follow your default settings.</li>
|
||||
</ol>
|
||||
<div style="user-select: text;background: #202020; overflow:auto;width:auto;border:solid gray;border-width:.1em .1em .1em .8em;padding:.2em .6em;"><pre style="margin: 0; line-height: 125%"><span style="color: #d0d0d0">{</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"breadcrumbs"</span><span style="color: #d0d0d0">:</span> <span style="color: #d0d0d0">{</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"down"</span><span style="color: #d0d0d0">:</span> <span style="color: #d0d0d0">[</span><span style="color: #ed9d13">"children"</span><span style="color: #d0d0d0">,</span> <span style="color: #ed9d13">"child"</span><span style="color: #d0d0d0">],</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"up"</span><span style="color: #d0d0d0">:</span> <span style="color: #d0d0d0">[</span><span style="color: #ed9d13">"parents"</span><span style="color: #d0d0d0">,</span> <span style="color: #ed9d13">"parent"</span><span style="color: #d0d0d0">],</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"jump"</span><span style="color: #d0d0d0">:</span> <span style="color: #d0d0d0">[</span><span style="color: #ed9d13">"jump"</span><span style="color: #d0d0d0">,</span> <span style="color: #ed9d13">"jumps"</span><span style="color: #d0d0d0">]</span>
|
||||
<span style="color: #d0d0d0">},</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"tags"</span><span style="color: #d0d0d0">:</span> <span style="color: #d0d0d0">{</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"#excalidraw"</span><span style="color: #d0d0d0">:</span> <span style="color: #d0d0d0">{</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"nodeColor"</span><span style="color: #d0d0d0">:</span> <span style="color: #ed9d13">"hsl(59, 80%, 77%)"</span><span style="color: #d0d0d0">,</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"gateColor"</span><span style="color: #d0d0d0">:</span> <span style="color: #ed9d13">"#fd7e14"</span><span style="color: #d0d0d0">,</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"borderColor"</span><span style="color: #d0d0d0">:</span> <span style="color: #ed9d13">"black"</span><span style="color: #d0d0d0">,</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"backgroundColor"</span><span style="color: #d0d0d0">:</span> <span style="color: #ed9d13">"rgba(50,50,50,0.5)"</span><span style="color: #d0d0d0">,</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"prefix"</span><span style="color: #d0d0d0">:</span> <span style="color: #ed9d13">"🎨 "</span>
|
||||
<span style="color: #d0d0d0">},</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"#dnp"</span><span style="color: #d0d0d0">:</span> <span style="color: #d0d0d0">{</span>
|
||||
<span style="color: #6ab825; font-weight: bold">"prefix"</span><span style="color: #d0d0d0">:</span> <span style="color: #ed9d13">"🗓 "</span>
|
||||
<span style="color: #d0d0d0">}</span>
|
||||
<span style="color: #d0d0d0">}</span>
|
||||
<span style="color: #d0d0d0">}</span>
|
||||
</pre></div>`
|
||||
};
|
||||
saveSettings = true;
|
||||
}
|
||||
|
||||
if(saveSettings) {
|
||||
ea.setScriptSettings(settings);
|
||||
}
|
||||
|
||||
const SHOW_CONFIRMATION_PROMPT = settings["Confirmation prompt at startup"].value;
|
||||
const MAX_ITEMS = Math.floor(settings["Max number of nodes/domain"].value)??40;
|
||||
@@ -192,13 +240,49 @@ const OBSIDIAN_NODE_BG_COLOR = settings["Non-breadcrumbs-node background color"]
|
||||
const OBSIDIAN_NODE_COLOR = settings["Non-breadcrumbs-node color"].value;
|
||||
const VIRTUAL_NODE_BG_COLOR = settings["Virtual-node background color"].value;
|
||||
const VIRTUAL_NODE_COLOR = settings["Virtual-node color"].value;
|
||||
const USE_ALIAS = settings["Display alias if available"].value;
|
||||
let formattingJSON = {};
|
||||
try {
|
||||
formattingJSON = JSON.parse(settings["Graph settings JSON"].value);
|
||||
} catch (e) {
|
||||
new Notice("Error reading graph settings JSON, see developer console for more information",4000);
|
||||
console.log(e);
|
||||
};
|
||||
const NODE_FORMATTING = formattingJSON?.tags??{};
|
||||
const FORMATTED_TAGS = Object.keys(NODE_FORMATTING);
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Load breadcrumbs hierarchies
|
||||
const HIERARCHIES = new Map();
|
||||
if(window.BCAPI) { //read breadcrumbs if available
|
||||
const getHierarchyFields = (direction) => {
|
||||
const values = new Set(direction);
|
||||
direction.forEach(
|
||||
d => BCAPI.plugin.settings.userHiers.forEach(
|
||||
h => h[d].forEach(
|
||||
x => values.add(x)
|
||||
)
|
||||
)
|
||||
);
|
||||
return Array.from(values);
|
||||
}
|
||||
HIERARCHIES.set("down",getHierarchyFields(["down"]));
|
||||
HIERARCHIES.set("up",getHierarchyFields(["up"]));
|
||||
HIERARCHIES.set("jump",getHierarchyFields(["prev","next"]));
|
||||
} else {
|
||||
HIERARCHIES.set("down",formattingJSON?.breadcrumbs?.down??["down","child","children"]);
|
||||
HIERARCHIES.set("up",formattingJSON?.breadcrumbs?.up??["up", "parent","parents"]);
|
||||
HIERARCHIES.set("jump",formattingJSON?.breadcrumbs?.jump??["jump", "jumps", "next", "previous"]);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Initialization
|
||||
//-------------------------------------------------------
|
||||
|
||||
if(SHOW_CONFIRMATION_PROMPT) {
|
||||
const result = await utils.inputPrompt("This will overwrite the current active drawing","type: 'ok' to Continue");
|
||||
const result = await utils.inputPrompt(
|
||||
"This will overwrite the current active drawing",
|
||||
"type: 'ok' to Continue"
|
||||
);
|
||||
if(result !== "ok") return;
|
||||
}
|
||||
|
||||
@@ -222,7 +306,9 @@ ea.getExcalidrawAPI().updateScene({
|
||||
});
|
||||
|
||||
ea.style.strokeColor = NODE_COLOR;
|
||||
ea.addText(0,0,"Open a document in another pane and click it to get started.\n\nIf you do not see the Breadcrumbs as expected,\ntry refreshing the index in BC matrix view.\n\nFor best experience enable 'Open in adjacent pane'\nin Excalidraw settings under 'Links and Transclusion'.", {textAlign:"center"});
|
||||
ea.addText(0,0,"Open a document in another pane and click it to get started.\n\n" +
|
||||
"For the best experience enable 'Open in adjacent pane'\nin Excalidraw settings " +
|
||||
"under 'Links and Transclusion'.", {textAlign:"center"});
|
||||
await ea.addElementsToView();
|
||||
ea.getExcalidrawAPI().zoomToFit();
|
||||
|
||||
@@ -236,19 +322,16 @@ new Notice("Brain Graph On");
|
||||
//-------------------------------------------------------
|
||||
// Supporting functions and classes
|
||||
//-------------------------------------------------------
|
||||
const getMatrixNeighbours = (node) => {
|
||||
try {
|
||||
return BCAPI.getMatrixNeighbours(node);
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
const getFilenameFromPath = (path) => {
|
||||
const mdFile = path.endsWith(".md");
|
||||
const filename = path.substring(path.lastIndexOf("/")+1);
|
||||
return mdFile ? filename.slice(0,-3) : filename;
|
||||
}
|
||||
|
||||
const joinRealsAndImplieds = (data) => {
|
||||
result = new Set();
|
||||
data.reals.forEach(i=>result.add(i.to));
|
||||
data.implieds.forEach(i=>result.add(i.to));
|
||||
return Array.from(result);
|
||||
const getExtension = (path) => {
|
||||
const lastDot = path.lastIndexOf(".");
|
||||
if(lastDot === -1) return "md";
|
||||
return path.slice(lastDot+1);
|
||||
}
|
||||
|
||||
const distinct = (data) => Array.from(new Set(data));
|
||||
@@ -320,8 +403,26 @@ class Layout {
|
||||
|
||||
class Node {
|
||||
constructor(spec) {
|
||||
this.spec = spec;
|
||||
const label = spec.file?.basename??spec.nodeTitle;
|
||||
const dvPage = spec.file ? DataviewAPI?.page(spec.file.path) : null;
|
||||
const tag = (dvPage?.file?.tags?.values??[]).filter(t=>FORMATTED_TAGS.some(x=>t.startsWith(x)))[0];
|
||||
if(tag) {
|
||||
const format = NODE_FORMATTING[FORMATTED_TAGS.filter(x=>tag.startsWith(x))[0]];
|
||||
spec.gateColor = format.gateColor ?? spec.gateColor;
|
||||
spec.backgroundColor = format.backgroundColor ?? spec.backgroundColor;
|
||||
spec.nodeColor = format.nodeColor ?? spec.nodeColor;
|
||||
spec.borderColor = format.borderColor ?? spec.borderColor;
|
||||
spec.prefix = format.prefix;
|
||||
}
|
||||
this.spec = spec;
|
||||
|
||||
const aliases = (spec.file && USE_ALIAS)
|
||||
? (dvPage?.file?.aliases?.values??[])
|
||||
: [];
|
||||
const label = (spec.prefix??"") + (aliases.length > 0
|
||||
? aliases[0]
|
||||
: (spec.file
|
||||
? (spec.file.extension === "md" ? spec.file.basename : spec.file.name)
|
||||
: spec.nodeTitle));
|
||||
this.label = label.length > spec.maxLabelLength
|
||||
? label.substring(0,spec.maxLabelLength-1) + "..."
|
||||
: label;
|
||||
@@ -355,6 +456,7 @@ class Node {
|
||||
? this.spec.backgroundColor
|
||||
: this.spec.virtualNodeBGColor;
|
||||
box.strokeColor = this.spec.borderColor;
|
||||
box.strokeStyle = this.spec.strokeStyle??"solid";
|
||||
|
||||
ea.style.strokeColor = this.spec.gateColor;
|
||||
ea.style.backgroundColor = this.spec.hasJumps ? this.spec.gateColor : "transparent";
|
||||
@@ -386,93 +488,205 @@ class Node {
|
||||
}
|
||||
|
||||
const addNodes = (nodesMap, root, nodes, layout, options) => {
|
||||
nodes.forEach(nodeTitle => {
|
||||
nodes.forEach(filePath => {
|
||||
const node = new Node({
|
||||
nodeTitle,
|
||||
file: app.metadataCache.getFirstLinkpathDest(nodeTitle,root.path),
|
||||
nodeTitle: getFilenameFromPath(filePath),
|
||||
file: app.vault.getAbstractFileByPath(filePath),
|
||||
hasChildren: false,
|
||||
hasParents: false,
|
||||
hasJumps: false,
|
||||
...options
|
||||
});
|
||||
nodesMap.set(nodeTitle,node);
|
||||
nodesMap.set(filePath,node);
|
||||
layout.nodes.push(node);
|
||||
});
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Breadcrumbs workaround to handle full filepath
|
||||
//-------------------------------------------------------
|
||||
const getPathOrSelf = (link,host) => {
|
||||
return (app.metadataCache.getFirstLinkpathDest(link,host)?.path)??link;
|
||||
}
|
||||
|
||||
const readDVField = (field,file) => {
|
||||
const res = new Set();
|
||||
if(field.values) {
|
||||
field.values.forEach(l=>{
|
||||
if(l.type === "file") res.add(getPathOrSelf(l.path,file.path));
|
||||
});
|
||||
return Array.from(res);
|
||||
}
|
||||
if(field.path) {
|
||||
return [getPathOrSelf(field.path,file.path)];
|
||||
}
|
||||
const m = field.matchAll(/[^[]*\[\[([^#\]\|]*)[^\]]*]]/g);
|
||||
while(!(r=m.next()).done) {
|
||||
if(r.value[1]) {
|
||||
res.add(getPathOrSelf(r.value[1],file.path));
|
||||
}
|
||||
}
|
||||
return Array.from(res);
|
||||
}
|
||||
|
||||
const getDVFieldLinks = (page,dir,retSet=false) => {
|
||||
const fields = HIERARCHIES.get(dir);
|
||||
const links = new Set();
|
||||
const processed = new Set();
|
||||
fields.forEach(f => {
|
||||
if(page[f] && !processed.has(f)) {
|
||||
processed.add(f);
|
||||
readDVField(page[f],page.file).forEach(l=>links.add(l))
|
||||
};
|
||||
});
|
||||
return retSet ? links : Array.from(links);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Event handler
|
||||
//-------------------------------------------------------
|
||||
window.brainGraphEventHandler = async (leaf) => {
|
||||
//sleep for 100ms
|
||||
//sleep for 100ms to give time for leaf.view.file to be set
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
|
||||
//-------------------------------------------------------
|
||||
//terminate event handler if view no longer exists or file has changed
|
||||
if(!window.excalidrawView?.file || window.excalidrawView.file.path !== window.excalidrawFile?.path) {
|
||||
removeEventHandler();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!leaf?.view?.file) return;
|
||||
const rootFile = leaf.view.file;
|
||||
|
||||
if (rootFile.path === window.excalidrawFile.path) return; //brainview drawing is active
|
||||
|
||||
//need to reinitialize ea because in the event handler ea provided by the script engine will no longer be available
|
||||
if(window.lastfilePath && window.lastfilePath === rootFile.path) return; //don't reload the file if it has not changed
|
||||
window.lastfilePath = rootFile.path;
|
||||
|
||||
//-------------------------------------------------------
|
||||
//I must reinitialize ea because in the event handler the script engine ea will no longer be available
|
||||
ea = ExcalidrawAutomate;
|
||||
ea.reset();
|
||||
ea.setView(window.excalidrawView);
|
||||
ea.style.fontFamily = FONT_FAMILY;
|
||||
ea.style.roughness = STROKE_ROUGHNESS;
|
||||
ea.style.strokeSharpness = STROKE_SHARPNESS;
|
||||
|
||||
if(!leaf?.view?.file) return;
|
||||
const file = leaf.view.file;
|
||||
|
||||
if (file.path === window.excalidrawFile.path) return; //brainview drawing is active
|
||||
|
||||
if(window.lastfilePath && window.lastfilePath === file.path) return; //don't reload the file if it has not changed
|
||||
window.lastfilePath = file.path;
|
||||
|
||||
ea.getExcalidrawAPI().updateScene({elements:[]});
|
||||
const centralNodeTitle = file.extension === "md" ? file.basename : file.name;
|
||||
|
||||
ea.style.verticalAlign = "middle";
|
||||
ea.style.strokeSharpness = "round";
|
||||
ea.style.fillStyle = "solid";
|
||||
|
||||
const rootFile = app.metadataCache.getFirstLinkpathDest(centralNodeTitle,"")
|
||||
const bc = getMatrixNeighbours(centralNodeTitle);
|
||||
|
||||
const parents = bc ? joinRealsAndImplieds(bc.up).filter(n=>n!==centralNodeTitle).slice(0,MAX_ITEMS) : [];
|
||||
const children = bc ? joinRealsAndImplieds(bc.down).filter(n=>n!==centralNodeTitle).slice(0,MAX_ITEMS) : [];
|
||||
const jumps = bc ? distinct(
|
||||
joinRealsAndImplieds(bc.next)
|
||||
.concat(joinRealsAndImplieds(bc.prev))
|
||||
).filter(n=>n!==centralNodeTitle)
|
||||
.slice(0,MAX_ITEMS) : [];
|
||||
const siblings = bc ? joinRealsAndImplieds(bc.same).filter(n=>n!==centralNodeTitle).slice(0,MAX_ITEMS) : []; //see breadcrumbs settings to finetune siblings
|
||||
|
||||
bclinks = new Set(parents.concat(children).concat(jumps).concat(siblings).concat([centralNodeTitle]));
|
||||
//adding links from the document, not explicitly declared as a breadcrumbs
|
||||
//this code assumes unique filenames (like breadcrumbs, thus will not handle non unique files well)
|
||||
//-------------------------------------------------------
|
||||
//build list of nodes
|
||||
const dvPage = DataviewAPI.page(rootFile.path); //workaround because
|
||||
const parentsSet = dvPage
|
||||
? getDVFieldLinks(dvPage,"up",true)
|
||||
: new Set();
|
||||
parentsSet.delete(rootFile.path);
|
||||
|
||||
const childrenSet = dvPage
|
||||
? getDVFieldLinks(dvPage,"down",true)
|
||||
: new Set();
|
||||
childrenSet.delete(rootFile.path);
|
||||
|
||||
const jumpsSet = dvPage
|
||||
? getDVFieldLinks(dvPage,"jump",true)
|
||||
: new Set();
|
||||
jumpsSet.delete(rootFile.path);
|
||||
|
||||
let backlinksSet = new Set(
|
||||
Object.keys((app.metadataCache.getBacklinksForFile(rootFile)?.data)??{})
|
||||
.map(l=>app.metadataCache.getFirstLinkpathDest(l,rootFile.path)?.path??l)
|
||||
);
|
||||
|
||||
backlinksSet.forEach(l=>{
|
||||
const page = DataviewAPI.page(l);
|
||||
if(page) {
|
||||
if(getDVFieldLinks(page,"down",true).has(rootFile.path)) {
|
||||
parentsSet.add(l);
|
||||
backlinksSet.delete(l);
|
||||
return;
|
||||
}
|
||||
if(getDVFieldLinks(page,"up",true).has(rootFile.path)) {
|
||||
childrenSet.add(l);
|
||||
backlinksSet.delete(l);
|
||||
return;
|
||||
}
|
||||
if(getDVFieldLinks(page,"jump", true).has(rootFile.path)) {
|
||||
jumpsSet.add(l);
|
||||
backlinksSet.delete(l);
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const parents = Array.from(parentsSet).slice(0,MAX_ITEMS);
|
||||
const children = Array.from(childrenSet).slice(0,MAX_ITEMS);
|
||||
const jumps = Array.from(jumpsSet).slice(0,MAX_ITEMS);
|
||||
|
||||
//adding links from the document, not explicitly declared as a breadcrumb
|
||||
const forwardLinks = INCLUDE_OBSIDIAN_LINKS
|
||||
? distinct(app.metadataCache
|
||||
.getLinks()[rootFile.path]?.map(l=>app.metadataCache.getFirstLinkpathDest(l.link,rootFile.path))
|
||||
.filter(f=>f && (!HIDE_ATTACHMENTS || f.extension === "md"))
|
||||
.map(f=>f.extension === "md" ? f.basename : f.name)
|
||||
.filter(l=>!bclinks.has(l))??[].slice(0,MAX_ITEMS))
|
||||
.getLinks()[rootFile.path]?.map(
|
||||
l=>app.metadataCache.getFirstLinkpathDest(l.link.match(/[^#]*/)[0],rootFile.path)??l.link.match(/[^#]*/)[0]
|
||||
).filter(f=>f && (!HIDE_ATTACHMENTS || (f.extension??getExtension(f)) === "md"))
|
||||
.map(f=>f.path??f)
|
||||
.filter(l=>!parentsSet.has(l) && !childrenSet.has(l) && !jumpsSet.has(l) && l!==rootFile.path && !backlinksSet.has(l))
|
||||
.slice(0,Math.max(MAX_ITEMS-children.length))
|
||||
)
|
||||
: [];
|
||||
const forwardLinksSet = new Set(forwardLinks);
|
||||
const forwardlinksSet = new Set(forwardLinks);
|
||||
|
||||
const backLinks = INCLUDE_OBSIDIAN_LINKS
|
||||
? distinct(Object
|
||||
.keys(app.metadataCache.getBacklinksForFile(rootFile)?.data??{})
|
||||
.map(l=>app.metadataCache.getFirstLinkpathDest(l,rootFile.path))
|
||||
.filter(f=>f && f.path !== window.excalidrawFile.path && (!HIDE_ATTACHMENTS || f.extension === "md"))
|
||||
.map(f=>f.extension === "md" ? f.basename : f.name)
|
||||
.filter(l=>!bclinks.has(l) && !forwardLinksSet.has(l))
|
||||
.slice(0,MAX_ITEMS))
|
||||
: [];
|
||||
const backLinksSet = new Set(backLinks);
|
||||
? Array.from(backlinksSet)
|
||||
.filter(l=>!parentsSet.has(l) && !childrenSet.has(l) &&
|
||||
!jumpsSet.has(l) && l!==rootFile.path && !forwardlinksSet.has(l))
|
||||
.slice(0,Math.max(MAX_ITEMS-parents.length))
|
||||
: [];
|
||||
backlinksSet = new Set(backLinks);
|
||||
|
||||
const sharedParents = INCLUDE_OBSIDIAN_LINKS
|
||||
? backLinks.concat(parents)
|
||||
: parents;
|
||||
|
||||
const siblings = distinct(
|
||||
sharedParents.map(p=>{
|
||||
const page = DataviewAPI.page(p);
|
||||
return page
|
||||
? getDVFieldLinks(page,"down")
|
||||
.filter(l=>!parentsSet.has(l) && !childrenSet.has(l) &&
|
||||
!jumpsSet.has(l) && l!==rootFile.path &&
|
||||
!forwardlinksSet.has(l) && !backlinksSet.has(l))
|
||||
: [];
|
||||
}).flat()
|
||||
).slice(0,MAX_ITEMS);
|
||||
const siblingsSet = new Set(siblings);
|
||||
|
||||
const inverseInferredSiblingsMap = new Map();
|
||||
const inferredSiblings = INCLUDE_OBSIDIAN_LINKS
|
||||
? distinct(
|
||||
sharedParents.map(p=>{
|
||||
f = app.vault.getAbstractFileByPath(p); //app.metadataCache.getFirstLinkpathDest(p,rootFile.path);
|
||||
if(!f) {
|
||||
return [];
|
||||
}
|
||||
const res = Object.keys((app.metadataCache.getBacklinksForFile(f)?.data)??{})
|
||||
.map(l=>app.metadataCache.getFirstLinkpathDest(l,rootFile.path)?.path??l)
|
||||
.filter(l=>!parentsSet.has(l) && !childrenSet.has(l) && !jumpsSet.has(l) &&
|
||||
l!==rootFile.path && !forwardlinksSet.has(l) && !backlinksSet.has(l) && !siblingsSet.has(l));
|
||||
res.forEach(r=>inverseInferredSiblingsMap.set(r,p));
|
||||
return res;
|
||||
}).flat()
|
||||
).slice(0,Math.max(MAX_ITEMS-siblings.length))
|
||||
: [];
|
||||
const inferredSiblingsSet = new Set(inferredSiblings);
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Generate layout and nodes
|
||||
const nodesMap = new Map();
|
||||
const linksMap = new Map();
|
||||
|
||||
|
||||
const lCenter = new Layout({
|
||||
origoX: 0,
|
||||
origoY: 0,
|
||||
@@ -484,7 +698,7 @@ window.brainGraphEventHandler = async (leaf) => {
|
||||
});
|
||||
|
||||
const manyChildren = (children.length + forwardLinks.length) >10;
|
||||
const manySiblings = siblings.length > 10;
|
||||
const manySiblings = (siblings.length + inferredSiblings.length) > 10;
|
||||
const singleParent = (parents.length + backLinks.length) <= 1
|
||||
|
||||
const lChildren = new Layout({
|
||||
@@ -544,7 +758,7 @@ window.brainGraphEventHandler = async (leaf) => {
|
||||
backgroundColor: CENTRAL_NODE_BG_COLOR,
|
||||
virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR
|
||||
});
|
||||
nodesMap.set(rootFile.basename,rootNode);
|
||||
nodesMap.set(rootFile.path,rootNode);
|
||||
lCenter.nodes.push(rootNode);
|
||||
|
||||
addNodes(
|
||||
@@ -582,14 +796,15 @@ window.brainGraphEventHandler = async (leaf) => {
|
||||
padding: PADDING,
|
||||
nodeColor: OBSIDIAN_NODE_COLOR,
|
||||
gateColor: GATE_COLOR,
|
||||
borderColor: "transparent",
|
||||
borderColor: OBSIDIAN_NODE_BG_COLOR,
|
||||
strokeStyle: "dotted",
|
||||
strokeWidth: 3,
|
||||
backgroundColor: OBSIDIAN_NODE_BG_COLOR,
|
||||
virtualNodeColor: VIRTUAL_NODE_COLOR,
|
||||
virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
addNodes(
|
||||
nodesMap,
|
||||
rootFile,
|
||||
@@ -625,14 +840,15 @@ window.brainGraphEventHandler = async (leaf) => {
|
||||
padding: PADDING,
|
||||
nodeColor: OBSIDIAN_NODE_COLOR,
|
||||
gateColor: GATE_COLOR,
|
||||
borderColor: "transparent",
|
||||
borderColor: OBSIDIAN_NODE_BG_COLOR,
|
||||
strokeStyle: "dotted",
|
||||
strokeWidth: 3,
|
||||
backgroundColor: OBSIDIAN_NODE_BG_COLOR,
|
||||
virtualNodeColor: VIRTUAL_NODE_COLOR,
|
||||
virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
addNodes(
|
||||
nodesMap,
|
||||
rootFile,
|
||||
@@ -675,32 +891,138 @@ window.brainGraphEventHandler = async (leaf) => {
|
||||
}
|
||||
);
|
||||
|
||||
Array.from(nodesMap.keys()).forEach(nodeTitle => {
|
||||
const node = nodesMap.get(nodeTitle);
|
||||
const bc = getMatrixNeighbours(nodeTitle);
|
||||
const parent = forwardLinksSet.has(nodeTitle) ? [rootFile.basename] : [];
|
||||
const parents = bc ? joinRealsAndImplieds(bc.up).concat(parent) : parent;
|
||||
const child = backLinksSet.has(nodeTitle) ? [rootFile.basename] : []
|
||||
const children = bc ? joinRealsAndImplieds(bc.down).concat(child) : child;
|
||||
const jumps = bc ? distinct(joinRealsAndImplieds(bc.next).concat(joinRealsAndImplieds(bc.prev))) : [];
|
||||
//siblings are left out in this case on purpose
|
||||
addNodes(
|
||||
nodesMap,
|
||||
rootFile,
|
||||
inferredSiblings,
|
||||
lSiblings,
|
||||
{
|
||||
fontSize: DISTANT_FONT_SIZE,
|
||||
jumpOnLeft: true,
|
||||
maxLabelLength: MAX_LABEL_LENGTH,
|
||||
gateRadius: GATE_RADIUS,
|
||||
gateOffset: GATE_OFFSET,
|
||||
padding: PADDING,
|
||||
nodeColor: OBSIDIAN_NODE_COLOR,
|
||||
gateColor: GATE_COLOR,
|
||||
borderColor: OBSIDIAN_NODE_BG_COLOR,
|
||||
strokeStyle: "dotted",
|
||||
strokeWidth: 3,
|
||||
backgroundColor: OBSIDIAN_NODE_BG_COLOR,
|
||||
virtualNodeColor: VIRTUAL_NODE_COLOR,
|
||||
virtualNodeBGColor: VIRTUAL_NODE_BG_COLOR
|
||||
}
|
||||
);
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Generate links for all displayed nodes
|
||||
const separator = "?"; //any character disallowed in filenames
|
||||
Array.from(nodesMap.keys()).forEach(filePath => {
|
||||
const node = nodesMap.get(filePath);
|
||||
const dvPage = DataviewAPI.page(filePath);
|
||||
|
||||
node.spec.hasChildren = children.length>0;
|
||||
node.spec.hasParents = parents.length>0;
|
||||
node.spec.hasJumps = jumps.length>0;
|
||||
//-------------------------------------------------------
|
||||
// Add links originating from node
|
||||
const parent = forwardlinksSet.has(filePath)
|
||||
? rootFile.path
|
||||
: inverseInferredSiblingsMap.get(filePath);
|
||||
const parents = dvPage
|
||||
? (parent ? getDVFieldLinks(dvPage,"up",true).add(parent) : getDVFieldLinks(dvPage,"up",true))
|
||||
: (parent ? new Set([parent]) : new Set());
|
||||
|
||||
const child = backlinksSet.has(filePath) ? rootFile.path : null
|
||||
const children = dvPage
|
||||
? (child ? getDVFieldLinks(dvPage,"down", true).add(child) : getDVFieldLinks(dvPage,"down", true))
|
||||
: (child ? new Set([child]) : new Set());
|
||||
|
||||
const jumps = dvPage
|
||||
? getDVFieldLinks(dvPage,"jump", true)
|
||||
: new Set();
|
||||
|
||||
//siblings are left out in this case on purpose
|
||||
|
||||
const addLinks = (nodes,relation) => nodes.forEach(n => {
|
||||
if(!nodesMap.has(n)) return;
|
||||
if(linksMap.has(`${nodeTitle}/${n}`)) return;
|
||||
linksMap.set(`${nodeTitle}/${n}`,relation);
|
||||
linksMap.set(`${n}/${nodeTitle}`,null);
|
||||
if(linksMap.has(filePath + separator + n)) return;
|
||||
linksMap.set(filePath + separator + n,relation);
|
||||
linksMap.set(n + separator + filePath,null);
|
||||
});
|
||||
|
||||
addLinks(parents,["parent","child"]);
|
||||
addLinks(children, ["child","parent"]);
|
||||
addLinks(jumps, ["jump","jump"]);
|
||||
|
||||
//-------------------------------------------------------
|
||||
//Set gate fill
|
||||
//- if node has outgoing links
|
||||
node.spec.hasChildren = node.spec.hasChildren || children.size>0;
|
||||
node.spec.hasParents = node.spec.hasParents || parents.size>0;
|
||||
node.spec.hasJumps = node.spec.hasJumps || jumps.size>0;
|
||||
|
||||
//- of the nodes on the other end of outgoing links
|
||||
// this is required on top of backlinks, to handle
|
||||
// nodes for referenced but not yet created pages
|
||||
parents.forEach(p=>{
|
||||
const n = nodesMap.get(p);
|
||||
if(n) n.spec.hasChildren = true;
|
||||
});
|
||||
|
||||
children.forEach(p=>{
|
||||
const n = nodesMap.get(p);
|
||||
if(n) n.spec.hasParents = true;
|
||||
});
|
||||
|
||||
jumps.forEach(p=>{
|
||||
const n = nodesMap.get(p);
|
||||
if(n) n.spec.hasJumps = true;
|
||||
});
|
||||
|
||||
//- if node has an incoming link from a node outside the visible graph
|
||||
if(node.spec.file && !(node.spec.hasParents && node.spec.hasChildren && node.hasJumps)) {
|
||||
const nodeBacklinks = new Set(
|
||||
Object.keys((app.metadataCache.getBacklinksForFile(node.spec.file)?.data)??{})
|
||||
.map(l=>app.metadataCache.getFirstLinkpathDest(l,filePath)?.path??l)
|
||||
);
|
||||
|
||||
nodeBacklinks.forEach(l=>{
|
||||
if(node.spec.hasParents && node.spec.hasChildren && node.hasJumps) return;
|
||||
const page = DataviewAPI.page(l);
|
||||
if(page) {
|
||||
if(getDVFieldLinks(page,"down",true).has(filePath)) {
|
||||
node.spec.hasParents = true;
|
||||
return;
|
||||
}
|
||||
if(getDVFieldLinks(page,"up",true).has(filePath)) {
|
||||
node.spec.hasChildren = true;
|
||||
return;
|
||||
}
|
||||
if(getDVFieldLinks(page,"jump",true).has(filePath)) {
|
||||
node.spec.hasJumps = true;
|
||||
return;
|
||||
}
|
||||
if(INCLUDE_OBSIDIAN_LINKS) {
|
||||
node.spec.hasParents = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(!node.spec.hasChildren && INCLUDE_OBSIDIAN_LINKS && node.spec.file) {
|
||||
const forwardLinks = app.metadataCache
|
||||
.getLinks()[filePath]?.map(
|
||||
l=>app.metadataCache.getFirstLinkpathDest(l.link.match(/[^#]*/)[0],rootFile.path)??l.link.match(/[^#]*/)[0]
|
||||
).filter(f=>f && (!HIDE_ATTACHMENTS || (f.extension??getExtension(f)) === "md"))
|
||||
.map(f=>f.path??f)
|
||||
.filter(l=>!parents.has(l) && !children.has(l) && !jumps.has(l) && l!==rootFile.path);
|
||||
if(forwardLinks.length > 0) {
|
||||
node.spec.hasChildren = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Render
|
||||
lCenter.render();
|
||||
lParents.render();
|
||||
lChildren.render();
|
||||
@@ -716,7 +1038,7 @@ window.brainGraphEventHandler = async (leaf) => {
|
||||
ea.style.strokeColor = LINK_COLOR;
|
||||
for([key,value] of linksMap) {
|
||||
if(value) {
|
||||
const k=key.split("/");
|
||||
const k=key.split(separator);
|
||||
const gate1 = getGate(k[0],value[0]);
|
||||
const gate2 = getGate(k[1],value[1]);
|
||||
ea.connectObjects(gate1, null, gate2, null, { startArrowHead: null, endArrowHead: null });
|
||||
@@ -732,8 +1054,13 @@ window.brainGraphEventHandler = async (leaf) => {
|
||||
ea.getExcalidrawAPI().zoomToFit();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
// Initiate event listner and trigger plex if file is open
|
||||
//-------------------------------------------------------
|
||||
app.workspace.on(EVENT, window.brainGraphEventHandler);
|
||||
|
||||
ea.targetView.semaphores.saving = true; //disable saving by setting this Excalidraw flag (not published API)
|
||||
|
||||
const mdLeaf = app.workspace.getLeavesOfType("markdown");
|
||||
if(mdLeaf.length>0) {
|
||||
window.brainGraphEventHandler(mdLeaf[0]);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
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.
|
||||
|
||||
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)
|
||||
Similar to Excalidraw standard <kbd>SHIFT+2</kbd> 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)
|
||||
|
||||
See documentation for more details:
|
||||
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -52,6 +52,7 @@ I would love to include your contribution in the script library. If you have a s
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20vertical%20distance%20between%20centers.svg"/></div>|[[#Fixed vertical distance between centers]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20vertical%20distance.svg"/></div>|[[#Fixed vertical distance]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Lighten%20background%20color.svg"/></div>|[[#Lighten background color]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Mindmap%20connector.svg"/></div>|[[#Mindmap connector]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Modify%20background%20color%20opacity.svg"/></div>|[[#Modify background color opacity]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Normalize%20Selected%20Arrows.svg"/></div>|[[#Normalize Selected Arrows]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/OCR%20-%20Optical%20Character%20Recognition.svg"/></div>|[[#OCR - Optical Character Recognition]]|
|
||||
@@ -68,7 +69,6 @@ I would love to include your contribution in the script library. If you have a s
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Stroke%20Width%20of%20Selected%20Elements.svg"/></div>|[[#Set Stroke Width of Selected Elements]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Text%20Alignment.svg"/></div>|[[#Set Text Alignment]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Split%20text%20by%20lines.svg"/></div>|[[#Split text by lines]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/TheBrain-navigation.svg"/></div>|[[#TheBrain-navigation]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.svg"/></div>|[[#Transfer TextElements to Excalidraw markdown metadata]]|
|
||||
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.svg"/></div>|[[#Zoom to Fit Selected Elements]]|
|
||||
|
||||
@@ -112,7 +112,7 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
|
||||
```excalidraw-script-install
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/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/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>
|
||||
<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 and fill style of selected Rectangles, Diamonds, Ellipses, Lines, Arrows and Freedraw.<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
|
||||
@@ -222,6 +222,12 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
|
||||
```
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/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/Lighten%20background%20color.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png'></td></tr></table>
|
||||
|
||||
## Mindmap connector
|
||||
```excalidraw-script-install
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Mindmap%20connector.md
|
||||
```
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/xllowl'>@xllowl</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/Mindmap%20connector.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script creates mindmap like lines (only right side and down available currently) for selected elements. The line will start according to the creation time of the elements. So you should create the header element first.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/mindmap%20connector.png'></td></tr></table>
|
||||
|
||||
## Modify background color opacity
|
||||
```excalidraw-script-install
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Modify%20background%20color%20opacity.md
|
||||
@@ -318,12 +324,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/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>
|
||||
|
||||
## TheBrain-navigation
|
||||
```excalidraw-script-install
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/TheBrain-navigation.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/TheBrain-navigation.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">An Excalidraw based graph user interface for your Vault. Requires the <a href='https://github.com/SkepticMystic/breadcrumbs'>Breadcrumbs plugin</a> to be installed and configured as well. Generates a user interface similar to that of <a href='https://TheBrain.com'>TheBrain</a>. Watch this introduction to this script on <a href='https://youtu.be/J4T5KHERH_o'>YouTube</a>.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/TheBrain.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
|
||||
@@ -334,4 +334,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 <kbd>SHIFT+2</kbd> 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>
|
||||
|
||||
@@ -50,6 +50,7 @@ I would love to include your contribution in the script library. If you have a s
|
||||
- [[#Fixed vertical distance between centers]]
|
||||
- [[#Fixed vertical distance]]
|
||||
- [[#Lighten background color]]
|
||||
- [[Mindmap connector]]
|
||||
- [[#Modify background color opacity]]
|
||||
- [[#Normalize Selected Arrows]]
|
||||
- [[#OCR - Optical Character Recognition]]
|
||||
@@ -220,6 +221,12 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
|
||||
```
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/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/Lighten%20background%20color.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">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.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png'></td></tr></table>
|
||||
|
||||
## Mindmap connector
|
||||
```excalidraw-script-install
|
||||
https://github.com/xllowl/obsidian-excalidraw-plugin/blob/master/ea-scripts/Mindmap%20connector.md
|
||||
```
|
||||
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/xllowl'>@xllowl</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/xllowl/obsidian-excalidraw-plugin/blob/master/ea-scripts/Mindmap%20connector.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script creates mindmap like lines(only right side available). The line will starts according to the creation time of the elements. So you may need to create the header element first.<br><img src='https://github.com/xllowl/obsidian-excalidraw-plugin/blob/master/images/mindmap%20connector.png'></td></tr></table>
|
||||
|
||||
## Modify background color opacity
|
||||
```excalidraw-script-install
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Modify%20background%20color%20opacity.md
|
||||
@@ -338,4 +345,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 <kbd>SHIFT+2</kbd> 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/Mindmap connector1.png
Normal file
BIN
images/Mindmap connector1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
images/mindmap connector.png
Normal file
BIN
images/mindmap connector.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 68 KiB |
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.6.24",
|
||||
"minAppVersion": "0.12.16",
|
||||
"version": "1.7.12",
|
||||
"minAppVersion": "0.15.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
"authorUrl": "https://zsolt.blog",
|
||||
|
||||
57
package.json
57
package.json
@@ -1,31 +1,39 @@
|
||||
{
|
||||
"name": "obsidian-excalidraw-plugin",
|
||||
"version": "1.6.13",
|
||||
"version": "1.7.11",
|
||||
"description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings",
|
||||
"main": "main.js",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib/**/*"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "cross-env NODE_ENV=development rollup --config rollup.config.js -w",
|
||||
"build": "cross-env NODE_ENV=production rollup --config rollup.config.js && terser main.js --compress toplevel=true,passes=2 --output main.js",
|
||||
"build": "cross-env NODE_ENV=production rollup --config rollup.config.js",
|
||||
"lib": "cross-env NODE_ENV=lib rollup --config rollup.config.js -w",
|
||||
"code:fix": "eslint --max-warnings=0 --ext .ts,.tsx ./src --fix"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zsviczian/excalidraw": "0.11.0-obsidian-9",
|
||||
"monkey-around": "^2.3.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "^5.0.0",
|
||||
"roughjs": "^4.5.2",
|
||||
"lz-string": "^1.4.4",
|
||||
"@types/lz-string": "^1.3.34",
|
||||
"clsx": "1.1.1"
|
||||
"@zsviczian/excalidraw": "0.12.0-obsidian-5",
|
||||
"clsx": "^1.1.1",
|
||||
"lz-string": "^1.4.4",
|
||||
"monkey-around": "^2.3.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "^5.0.1",
|
||||
"roughjs": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.16.12",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"@babel/preset-react": "^7.16.7",
|
||||
"@excalidraw/eslint-config": "1.0.0",
|
||||
"@excalidraw/prettier-config": "1.0.2",
|
||||
"@popperjs/core": "^2.11.5",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^21.0.1",
|
||||
"@rollup/plugin-node-resolve": "^13.1.3",
|
||||
@@ -33,22 +41,25 @@
|
||||
"@rollup/plugin-typescript": "^8.3.0",
|
||||
"@types/js-beautify": "^1.13.3",
|
||||
"@types/node": "^15.12.4",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@popperjs/core": "^2.11.2",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@zerollup/ts-transform-paths": "^1.7.18",
|
||||
"cross-env": "^7.0.3",
|
||||
"html2canvas": "^1.4.0",
|
||||
"nanoid": "^3.1.31",
|
||||
"obsidian": "^0.14.4",
|
||||
"rollup": "^2.70.1",
|
||||
"rollup-plugin-visualizer": "^5.6.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.5.5",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"html2canvas": "^1.4.0",
|
||||
"nanoid": "^4.0.0",
|
||||
"obsidian": "^0.15.4",
|
||||
"prettier": "^2.5.1",
|
||||
"@excalidraw/eslint-config": "1.0.0",
|
||||
"@excalidraw/prettier-config": "1.0.2"
|
||||
"rollup": "^2.70.1",
|
||||
"rollup-plugin-copy": "^3.4.0",
|
||||
"rollup-plugin-postprocess": "github:brettz9/rollup-plugin-postprocess#update",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.31.2",
|
||||
"rollup-plugin-visualizer": "^5.6.0",
|
||||
"rollup-plugin-web-worker-loader": "^1.6.1",
|
||||
"tslib": "^2.3.1",
|
||||
"ttypescript": "^1.5.13",
|
||||
"typescript": "^4.5.5"
|
||||
},
|
||||
"resolutions": {
|
||||
"@typescript-eslint/typescript-estree": "5.3.0"
|
||||
|
||||
32
prod.postprocess.config.js
Normal file
32
prod.postprocess.config.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import fs from'fs';
|
||||
import LZString from 'lz-string';
|
||||
|
||||
const excalidraw_pkg = fs.readFileSync("./node_modules/@zsviczian/excalidraw/dist/excalidraw.production.min.js", "utf8");
|
||||
const react_pkg = fs.readFileSync("./node_modules/react/umd/react.production.min.js", "utf8");
|
||||
const reactdom_pkg = fs.readFileSync("./node_modules/react-dom/umd/react-dom.production.min.js", "utf8");
|
||||
const lzstring_pkg = fs.readFileSync("./node_modules/lz-string/libs/lz-string.min.js", "utf8");
|
||||
const mainjs = fs.readFileSync("main.js", "utf8")
|
||||
|
||||
const packageString = lzstring_pkg+'const EXCALIDRAW_PACKAGES="' + LZString.compressToBase64(react_pkg + reactdom_pkg + excalidraw_pkg) +'";var ExcalidrawPackageLoader=(d=document)=>{if(!d.getElementById("excalidraw-script")){const script=d.createElement("script");script.type="text/javascript";script.id="excalidraw-script";script.text=LZString.decompressFromBase64(EXCALIDRAW_PACKAGES);d.body.appendChild(script);}};ExcalidrawPackageLoader();';
|
||||
|
||||
fs.writeFileSync(
|
||||
"main2.js",
|
||||
mainjs
|
||||
.replace('(require("react"))','')
|
||||
.replace('"use strict";','"use strict";' + packageString),
|
||||
{
|
||||
encoding: "utf8",
|
||||
flag: "w",
|
||||
mode: 0o666
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
export default ({
|
||||
input: 'foo',
|
||||
plugins: [],
|
||||
output: [{
|
||||
file: 'foo.js',
|
||||
format: 'es'
|
||||
}]
|
||||
});
|
||||
@@ -5,19 +5,60 @@ import { env } from "process";
|
||||
import babel from '@rollup/plugin-babel';
|
||||
import replace from "@rollup/plugin-replace";
|
||||
import { terser } from "rollup-plugin-terser";
|
||||
import copy from "rollup-plugin-copy";
|
||||
import ttypescript from "ttypescript";
|
||||
import typescript2 from "rollup-plugin-typescript2";
|
||||
import webWorker from "rollup-plugin-web-worker-loader";
|
||||
import fs from'fs';
|
||||
import LZString from 'lz-string';
|
||||
import postprocess from 'rollup-plugin-postprocess';
|
||||
|
||||
const isProd = (process.env.NODE_ENV === "production");
|
||||
console.log("Is production", isProd);
|
||||
|
||||
export default {
|
||||
const excalidraw_pkg = isProd
|
||||
? fs.readFileSync("./node_modules/@zsviczian/excalidraw/dist/excalidraw.production.min.js", "utf8")
|
||||
: fs.readFileSync("./node_modules/@zsviczian/excalidraw/dist/excalidraw.development.js", "utf8");
|
||||
const react_pkg = isProd
|
||||
? fs.readFileSync("./node_modules/react/umd/react.production.min.js", "utf8")
|
||||
: fs.readFileSync("./node_modules/react/umd/react.development.js", "utf8");
|
||||
const reactdom_pkg = isProd
|
||||
? fs.readFileSync("./node_modules/react-dom/umd/react-dom.production.min.js", "utf8")
|
||||
: fs.readFileSync("./node_modules/react-dom/umd/react-dom.development.js", "utf8");
|
||||
const lzstring_pkg = fs.readFileSync("./node_modules/lz-string/libs/lz-string.min.js", "utf8");
|
||||
|
||||
const manifestStr = fs.readFileSync("manifest.json", "utf-8");
|
||||
const manifest = JSON.parse(manifestStr);
|
||||
console.log(manifest.version);
|
||||
|
||||
const packageString = ';'+lzstring_pkg+'const EXCALIDRAW_PACKAGES = "' + LZString.compressToBase64(react_pkg + reactdom_pkg + excalidraw_pkg) + '";' +
|
||||
'const {react, reactDOM, excalidrawLib} = window.eval.call(window, `(function() {' +
|
||||
'${LZString.decompressFromBase64(EXCALIDRAW_PACKAGES)};' +
|
||||
'return {react:React, reactDOM:ReactDOM, excalidrawLib: ExcalidrawLib};})();`);' +
|
||||
'const PLUGIN_VERSION="'+manifest.version+'";';
|
||||
|
||||
|
||||
|
||||
const BASE_CONFIG = {
|
||||
input: 'src/main.ts',
|
||||
external: ['obsidian', '@zsviczian/excalidraw', 'react', 'react-dom'],
|
||||
}
|
||||
|
||||
const getRollupPlugins = (tsconfig, ...plugins) =>
|
||||
[
|
||||
typescript2(tsconfig),
|
||||
nodeResolve({ browser: true }),
|
||||
commonjs(),
|
||||
webWorker({ inline: true, forceInline: true, targetPlatform: "browser" }),
|
||||
].concat(plugins);
|
||||
|
||||
const BUILD_CONFIG = {
|
||||
...BASE_CONFIG,
|
||||
output: {
|
||||
dir: '.',
|
||||
sourcemap: 'inline',
|
||||
sourcemap: isProd?false:'inline',
|
||||
format: 'cjs',
|
||||
exports: 'default',
|
||||
},
|
||||
external: ['obsidian'],
|
||||
plugins: [
|
||||
replace({
|
||||
preventAssignment: true,
|
||||
@@ -27,10 +68,46 @@ export default {
|
||||
exclude: "node_modules/**"
|
||||
}),
|
||||
commonjs(),
|
||||
nodeResolve({ browser: true, preferBuiltins: true }),
|
||||
nodeResolve({ browser: true, preferBuiltins: false }),
|
||||
typescript({inlineSources: !isProd}),
|
||||
...isProd ? [
|
||||
terser({toplevel: true, compress: {passes: 2}})
|
||||
] : []
|
||||
...isProd
|
||||
? [
|
||||
terser({toplevel: false, compress: {passes: 2}}),
|
||||
//!postprocess - the version available on npmjs does not work, need this update:
|
||||
// npm install brettz9/rollup-plugin-postprocess#update --save-dev
|
||||
// https://github.com/developit/rollup-plugin-postprocess/issues/10
|
||||
postprocess([
|
||||
[/,React=require\("react"\);/, packageString],
|
||||
])
|
||||
]
|
||||
: [
|
||||
postprocess([
|
||||
[/var React = require\('react'\);/, packageString],
|
||||
])
|
||||
],
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const LIB_CONFIG = {
|
||||
...BASE_CONFIG,
|
||||
input: "src/index.ts",
|
||||
output: {
|
||||
dir: "lib",
|
||||
sourcemap: true,
|
||||
format: "cjs",
|
||||
name: "Excalidraw (Library)",
|
||||
},
|
||||
plugins: getRollupPlugins(
|
||||
{ tsconfig: "tsconfig-lib.json", typescript: ttypescript },
|
||||
copy({ targets: [{ src: "src/*.d.ts", dest: "lib/typings" }] })
|
||||
),
|
||||
}
|
||||
|
||||
let config = [];
|
||||
if(process.env.NODE_ENV === "lib") {
|
||||
config.push(LIB_CONFIG);
|
||||
} else {
|
||||
config.push(BUILD_CONFIG);
|
||||
}
|
||||
|
||||
export default config;
|
||||
@@ -34,6 +34,8 @@ import {
|
||||
svgToBase64,
|
||||
} from "./utils/Utils";
|
||||
|
||||
const THEME_FILTER = "invert(100%) hue-rotate(180deg) saturate(1.25)";
|
||||
|
||||
export declare type MimeType =
|
||||
| "image/svg+xml"
|
||||
| "image/png"
|
||||
@@ -60,6 +62,8 @@ export class EmbeddedFile {
|
||||
public mimeType: MimeType = "application/octet-stream";
|
||||
public size: Size = { height: 0, width: 0 };
|
||||
public linkParts: LinkParts;
|
||||
private hostPath: string;
|
||||
public attemptCounter: number = 0;
|
||||
/*public isHyperlink: boolean = false;*/
|
||||
|
||||
constructor(plugin: ExcalidrawPlugin, hostPath: string, imgPath: string) {
|
||||
@@ -77,6 +81,7 @@ export class EmbeddedFile {
|
||||
this.imgInverted = this.img = "";
|
||||
this.mtime = 0;
|
||||
this.linkParts = getLinkParts(imgPath);
|
||||
this.hostPath = hostPath;
|
||||
if (!this.linkParts.path) {
|
||||
new Notice(`Excalidraw Error\nIncorrect embedded filename: ${imgPath}`);
|
||||
return;
|
||||
@@ -92,16 +97,25 @@ export class EmbeddedFile {
|
||||
hostPath,
|
||||
);
|
||||
if (!this.file) {
|
||||
new Notice(
|
||||
`Excalidraw Warning: could not find image file: ${imgPath}`,
|
||||
5000,
|
||||
);
|
||||
if(this.attemptCounter++ === 0) {
|
||||
new Notice(
|
||||
`Excalidraw Warning: could not find image file: ${imgPath}`,
|
||||
5000,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fileChanged(): boolean {
|
||||
if (!this.file) {
|
||||
return false;
|
||||
this.file = this.plugin.app.metadataCache.getFirstLinkpathDest(
|
||||
this.linkParts.path,
|
||||
this.hostPath,
|
||||
); // maybe the file has synchronized in the mean time
|
||||
if(!this.file) {
|
||||
this.attemptCounter++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return this.mtime != this.file.stat.mtime;
|
||||
}
|
||||
@@ -135,7 +149,14 @@ export class EmbeddedFile {
|
||||
|
||||
public isLoaded(isDark: boolean): boolean {
|
||||
if (!this.file) {
|
||||
return true;
|
||||
this.file = this.plugin.app.metadataCache.getFirstLinkpathDest(
|
||||
this.linkParts.path,
|
||||
this.hostPath,
|
||||
); // maybe the file has synchronized in the mean time
|
||||
if(!this.file) {
|
||||
this.attemptCounter++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this.fileChanged()) {
|
||||
return false;
|
||||
@@ -162,7 +183,6 @@ export class EmbeddedFile {
|
||||
|
||||
export class EmbeddedFilesLoader {
|
||||
private plugin: ExcalidrawPlugin;
|
||||
private processedFiles: Map<string, number> = new Map<string, number>();
|
||||
private isDark: boolean;
|
||||
public terminate = false;
|
||||
public uid: string;
|
||||
@@ -173,7 +193,7 @@ export class EmbeddedFilesLoader {
|
||||
this.uid = nanoid();
|
||||
}
|
||||
|
||||
public async getObsidianImage(inFile: TFile | EmbeddedFile): Promise<{
|
||||
public async getObsidianImage(inFile: TFile | EmbeddedFile, depth: number): Promise<{
|
||||
mimeType: MimeType;
|
||||
fileId: FileId;
|
||||
dataURL: DataURL;
|
||||
@@ -196,15 +216,7 @@ export class EmbeddedFilesLoader {
|
||||
width: this.plugin.settings.mdSVGwidth,
|
||||
height: this.plugin.settings.mdSVGmaxHeight,
|
||||
};
|
||||
//to block infinite loop of recursive loading of images
|
||||
const count = this.processedFiles.has(file.path)
|
||||
? this.processedFiles.get(file.path)
|
||||
: 0;
|
||||
if (file.extension === "md" && count > 2) {
|
||||
new Notice(t("INFINITE_LOOP_WARNING") + file.path, 6000);
|
||||
return null;
|
||||
}
|
||||
this.processedFiles.set(file.path, count + 1);
|
||||
|
||||
let hasSVGwithBitmap = false;
|
||||
const app = this.plugin.app;
|
||||
const isExcalidrawFile = this.plugin.isExcalidrawFile(file);
|
||||
@@ -240,6 +252,7 @@ export class EmbeddedFilesLoader {
|
||||
null,
|
||||
[],
|
||||
this.plugin,
|
||||
depth+1,
|
||||
getSVGPadding(this.plugin, file),
|
||||
);
|
||||
//https://stackoverflow.com/questions/51154171/remove-css-filter-on-child-elements
|
||||
@@ -249,8 +262,7 @@ export class EmbeddedFilesLoader {
|
||||
if (imageList.length > 0) {
|
||||
hasSVGwithBitmap = true;
|
||||
}
|
||||
if (hasSVGwithBitmap && isDark) {
|
||||
const THEME_FILTER = "invert(100%) hue-rotate(180deg) saturate(1.25)";
|
||||
if (hasSVGwithBitmap && isDark) {
|
||||
imageList.forEach((i) => {
|
||||
const id = i.parentElement?.id;
|
||||
svg.querySelectorAll(`use[href='#${id}']`).forEach((u) => {
|
||||
@@ -291,17 +303,20 @@ export class EmbeddedFilesLoader {
|
||||
mimeType = "application/octet-stream";
|
||||
}
|
||||
}
|
||||
const dataURL =
|
||||
let dataURL =
|
||||
excalidrawSVG ??
|
||||
(file.extension === "svg"
|
||||
? await getSVGData(app, file)
|
||||
: file.extension === "md"
|
||||
? await convertMarkdownToSVG(this.plugin, file, linkParts)
|
||||
? null
|
||||
: await getDataURL(ab, mimeType));
|
||||
const size = await getImageSize(
|
||||
excalidrawSVG ??
|
||||
(file.extension === "md" ? dataURL : app.vault.getResourcePath(file)),
|
||||
);
|
||||
|
||||
if(!dataURL) {
|
||||
const result = await this.convertMarkdownToSVG(this.plugin, file, linkParts);
|
||||
dataURL = result.dataURL;
|
||||
hasSVGwithBitmap = result.hasSVGwithBitmap;
|
||||
}
|
||||
const size = await getImageSize(dataURL);
|
||||
return {
|
||||
mimeType,
|
||||
fileId: await generateIdFromFile(ab),
|
||||
@@ -315,7 +330,12 @@ export class EmbeddedFilesLoader {
|
||||
public async loadSceneFiles(
|
||||
excalidrawData: ExcalidrawData,
|
||||
addFiles: Function,
|
||||
depth:number
|
||||
) {
|
||||
if(depth > 4) {
|
||||
new Notice(t("INFINITE_LOOP_WARNING")+depth.toString(), 6000);
|
||||
return;
|
||||
}
|
||||
const entries = excalidrawData.getFileEntries();
|
||||
//debug({where:"EmbeddedFileLoader.loadSceneFiles",uid:this.uid,isDark:this.isDark,sceneTheme:excalidrawData.scene.appState.theme});
|
||||
if (this.isDark === undefined) {
|
||||
@@ -327,7 +347,7 @@ export class EmbeddedFilesLoader {
|
||||
const embeddedFile: EmbeddedFile = entry.value[1];
|
||||
if (!embeddedFile.isLoaded(this.isDark)) {
|
||||
//debug({where:"EmbeddedFileLoader.loadSceneFiles",uid:this.uid,status:"embedded Files are not loaded"});
|
||||
const data = await this.getObsidianImage(embeddedFile);
|
||||
const data = await this.getObsidianImage(embeddedFile, depth);
|
||||
if (data) {
|
||||
files.push({
|
||||
mimeType: data.mimeType,
|
||||
@@ -380,6 +400,224 @@ export class EmbeddedFilesLoader {
|
||||
errorlog({ where: "EmbeddedFileLoader.loadSceneFiles", error: e });
|
||||
}
|
||||
}
|
||||
|
||||
private async convertMarkdownToSVG(
|
||||
plugin: ExcalidrawPlugin,
|
||||
file: TFile,
|
||||
linkParts: LinkParts,
|
||||
): Promise<{dataURL: DataURL, hasSVGwithBitmap:boolean}> {
|
||||
//1.
|
||||
//get the markdown text
|
||||
let hasSVGwithBitmap = false;
|
||||
const transclusion = await getTransclusion(linkParts, plugin.app, file);
|
||||
let text = (transclusion.leadingHashes??"") + transclusion.contents;
|
||||
if (text === "") {
|
||||
text =
|
||||
"# Empty markdown file\nCTRL+Click here to open the file for editing in the current active pane, or CTRL+SHIFT+Click to open it in an adjacent pane.";
|
||||
}
|
||||
|
||||
//2.
|
||||
//get styles
|
||||
const fileCache = plugin.app.metadataCache.getFileCache(file);
|
||||
let fontDef: string;
|
||||
let fontName = plugin.settings.mdFont;
|
||||
if (
|
||||
fileCache?.frontmatter &&
|
||||
fileCache.frontmatter[FRONTMATTER_KEY_FONT] != null
|
||||
) {
|
||||
fontName = fileCache.frontmatter[FRONTMATTER_KEY_FONT];
|
||||
}
|
||||
switch (fontName) {
|
||||
case "Virgil":
|
||||
fontDef = VIRGIL_FONT;
|
||||
break;
|
||||
case "Cascadia":
|
||||
fontDef = CASCADIA_FONT;
|
||||
break;
|
||||
case "":
|
||||
fontDef = "";
|
||||
break;
|
||||
default:
|
||||
const font = await getFontDataURL(plugin.app, fontName, file.path);
|
||||
fontDef = font.fontDef;
|
||||
fontName = font.fontName;
|
||||
}
|
||||
|
||||
const fontColor = fileCache?.frontmatter
|
||||
? fileCache.frontmatter[FRONTMATTER_KEY_FONTCOLOR] ??
|
||||
plugin.settings.mdFontColor
|
||||
: plugin.settings.mdFontColor;
|
||||
|
||||
let style = fileCache?.frontmatter
|
||||
? fileCache.frontmatter[FRONTMATTER_KEY_MD_STYLE] ?? ""
|
||||
: "";
|
||||
let frontmatterCSSisAfile = false;
|
||||
if (style && style != "") {
|
||||
const f = plugin.app.metadataCache.getFirstLinkpathDest(style, file.path);
|
||||
if (f) {
|
||||
style = await plugin.app.vault.read(f);
|
||||
frontmatterCSSisAfile = true;
|
||||
}
|
||||
}
|
||||
if (!frontmatterCSSisAfile) {
|
||||
if (plugin.settings.mdCSS && plugin.settings.mdCSS !== "") {
|
||||
const f = plugin.app.metadataCache.getFirstLinkpathDest(
|
||||
plugin.settings.mdCSS,
|
||||
file.path,
|
||||
);
|
||||
style += f ? `\n${await plugin.app.vault.read(f)}` : DEFAULT_MD_EMBED_CSS;
|
||||
} else {
|
||||
style += DEFAULT_MD_EMBED_CSS;
|
||||
}
|
||||
}
|
||||
|
||||
const borderColor = fileCache?.frontmatter
|
||||
? fileCache.frontmatter[FRONTMATTER_KEY_BORDERCOLOR] ??
|
||||
plugin.settings.mdBorderColor
|
||||
: plugin.settings.mdBorderColor;
|
||||
|
||||
if (borderColor && borderColor !== "" && !style.match(/svg/i)) {
|
||||
style += `svg{border:2px solid;color:${borderColor};transform:scale(.95)}`;
|
||||
}
|
||||
|
||||
//3.
|
||||
//SVG helper functions
|
||||
//the SVG will first have ~infinite height. After sizing this will be reduced
|
||||
let svgStyle = ` width="${linkParts.width}px" height="100000"`;
|
||||
let foreignObjectStyle = ` width="${linkParts.width}px" height="100%"`;
|
||||
|
||||
const svg = (xml: string, xmlFooter: string, style?: string) =>
|
||||
`<svg xmlns="http://www.w3.org/2000/svg"${svgStyle}>${
|
||||
style ? `<style>${style}</style>` : ""
|
||||
}<foreignObject x="0" y="0"${foreignObjectStyle}>${xml}${
|
||||
xmlFooter //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/286#issuecomment-982179639
|
||||
}</foreignObject>${
|
||||
fontDef !== "" ? `<defs><style>${fontDef}</style></defs>` : ""
|
||||
}</svg>`;
|
||||
|
||||
//4.
|
||||
//create document div - this will be the contents of the foreign object
|
||||
const mdDIV = createDiv();
|
||||
mdDIV.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
||||
mdDIV.setAttribute("class", "excalidraw-md-host");
|
||||
// mdDIV.setAttribute("style",style);
|
||||
if (fontName !== "") {
|
||||
mdDIV.style.fontFamily = fontName;
|
||||
}
|
||||
mdDIV.style.overflow = "auto";
|
||||
mdDIV.style.display = "block";
|
||||
mdDIV.style.color = fontColor && fontColor !== "" ? fontColor : "initial";
|
||||
|
||||
await MarkdownRenderer.renderMarkdown(text, mdDIV, file.path, plugin);
|
||||
|
||||
mdDIV
|
||||
.querySelectorAll(":scope > *[class^='frontmatter']")
|
||||
.forEach((el) => mdDIV.removeChild(el));
|
||||
|
||||
const internalEmbeds = Array.from(mdDIV.querySelectorAll("span[class='internal-embed']"))
|
||||
for(let i=0;i<internalEmbeds.length;i++) {
|
||||
const el = internalEmbeds[i];
|
||||
const src = el.getAttribute("src");
|
||||
if(!src) continue;
|
||||
const width = el.getAttribute("width");
|
||||
const height = el.getAttribute("height");
|
||||
const ef = new EmbeddedFile(plugin,file.path,src);
|
||||
//const f = app.metadataCache.getFirstLinkpathDest(src.split("#")[0],file.path);
|
||||
if(!ef.file) continue;
|
||||
const embeddedFile = await this.getObsidianImage(ef,1);
|
||||
const img = createEl("img");
|
||||
if(width) img.setAttribute("width", width);
|
||||
if(height) img.setAttribute("height", height);
|
||||
img.src = embeddedFile.dataURL;
|
||||
el.replaceWith(img);
|
||||
}
|
||||
|
||||
//5.1
|
||||
//get SVG size.
|
||||
//First I need to create a fully self contained copy of the document to convert
|
||||
//blank styles into inline styles using computedStyle
|
||||
const iframeHost = document.body.createDiv();
|
||||
iframeHost.style.display = "none";
|
||||
const iframe = iframeHost.createEl("iframe");
|
||||
const iframeDoc = iframe.contentWindow.document;
|
||||
if (style) {
|
||||
const styleEl = iframeDoc.createElement("style");
|
||||
styleEl.type = "text/css";
|
||||
styleEl.innerHTML = style;
|
||||
iframeDoc.head.appendChild(styleEl);
|
||||
}
|
||||
const stylingDIV = iframeDoc.importNode(mdDIV, true);
|
||||
iframeDoc.body.appendChild(stylingDIV);
|
||||
const footerDIV = createDiv();
|
||||
footerDIV.setAttribute("class", "excalidraw-md-footer");
|
||||
iframeDoc.body.appendChild(footerDIV);
|
||||
|
||||
iframeDoc.body.querySelectorAll("*").forEach((el: HTMLElement) => {
|
||||
const elementStyle = el.style;
|
||||
const computedStyle = window.getComputedStyle(el);
|
||||
let style = "";
|
||||
for (const prop in elementStyle) {
|
||||
if (elementStyle.hasOwnProperty(prop)) {
|
||||
style += `${prop}: ${computedStyle[prop]};`;
|
||||
}
|
||||
}
|
||||
el.setAttribute("style", style);
|
||||
});
|
||||
|
||||
const xmlINiframe = new XMLSerializer().serializeToString(stylingDIV);
|
||||
const xmlFooter = new XMLSerializer().serializeToString(footerDIV);
|
||||
document.body.removeChild(iframeHost);
|
||||
|
||||
//5.2
|
||||
//get SVG size
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(
|
||||
svg(xmlINiframe, xmlFooter),
|
||||
"image/svg+xml",
|
||||
);
|
||||
const svgEl = doc.firstElementChild;
|
||||
const host = createDiv();
|
||||
host.appendChild(svgEl);
|
||||
document.body.appendChild(host);
|
||||
const footerHeight = svgEl.querySelector(
|
||||
".excalidraw-md-footer",
|
||||
).scrollHeight;
|
||||
const height =
|
||||
svgEl.querySelector(".excalidraw-md-host").scrollHeight + footerHeight;
|
||||
const svgHeight = height <= linkParts.height ? height : linkParts.height;
|
||||
document.body.removeChild(host);
|
||||
|
||||
//finalize SVG
|
||||
svgStyle = ` width="${linkParts.width}px" height="${svgHeight}px"`;
|
||||
foreignObjectStyle = ` width="${linkParts.width}px" height="${svgHeight}px"`;
|
||||
mdDIV.style.height = `${svgHeight - footerHeight}px`;
|
||||
mdDIV.style.overflow = "hidden";
|
||||
|
||||
const imageList = mdDIV.querySelectorAll(
|
||||
"img:not([src^='data:image/svg+xml'])",
|
||||
);
|
||||
if (imageList.length > 0) {
|
||||
hasSVGwithBitmap = true;
|
||||
}
|
||||
if (hasSVGwithBitmap && this.isDark) {
|
||||
imageList.forEach(img => {
|
||||
if(img instanceof HTMLImageElement) {
|
||||
img.style.filter = THEME_FILTER;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const xml = new XMLSerializer().serializeToString(mdDIV);
|
||||
const finalSVG = svg(xml, '<div class="excalidraw-md-footer"></div>', style);
|
||||
plugin.ea.mostRecentMarkdownSVG = parser.parseFromString(
|
||||
finalSVG,
|
||||
"image/svg+xml",
|
||||
).firstElementChild as SVGSVGElement;
|
||||
return {
|
||||
dataURL: svgToBase64(finalSVG) as DataURL,
|
||||
hasSVGwithBitmap
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const getSVGData = async (app: App, file: TFile): Promise<DataURL> => {
|
||||
@@ -387,185 +625,6 @@ const getSVGData = async (app: App, file: TFile): Promise<DataURL> => {
|
||||
return svgToBase64(svg) as DataURL;
|
||||
};
|
||||
|
||||
const convertMarkdownToSVG = async (
|
||||
plugin: ExcalidrawPlugin,
|
||||
file: TFile,
|
||||
linkParts: LinkParts,
|
||||
): Promise<DataURL> => {
|
||||
//1.
|
||||
//get the markdown text
|
||||
let text = (await getTransclusion(linkParts, plugin.app, file)).contents;
|
||||
if (text === "") {
|
||||
text =
|
||||
"# Empty markdown file\nCTRL+Click here to open the file for editing in the current active pane, or CTRL+SHIFT+Click to open it in an adjacent pane.";
|
||||
}
|
||||
|
||||
//2.
|
||||
//get styles
|
||||
const fileCache = plugin.app.metadataCache.getFileCache(file);
|
||||
let fontDef: string;
|
||||
let fontName = plugin.settings.mdFont;
|
||||
if (
|
||||
fileCache?.frontmatter &&
|
||||
fileCache.frontmatter[FRONTMATTER_KEY_FONT] != null
|
||||
) {
|
||||
fontName = fileCache.frontmatter[FRONTMATTER_KEY_FONT];
|
||||
}
|
||||
switch (fontName) {
|
||||
case "Virgil":
|
||||
fontDef = VIRGIL_FONT;
|
||||
break;
|
||||
case "Cascadia":
|
||||
fontDef = CASCADIA_FONT;
|
||||
break;
|
||||
case "":
|
||||
fontDef = "";
|
||||
break;
|
||||
default:
|
||||
const font = await getFontDataURL(plugin.app, fontName, file.path);
|
||||
fontDef = font.fontDef;
|
||||
fontName = font.fontName;
|
||||
}
|
||||
|
||||
const fontColor = fileCache?.frontmatter
|
||||
? fileCache.frontmatter[FRONTMATTER_KEY_FONTCOLOR] ??
|
||||
plugin.settings.mdFontColor
|
||||
: plugin.settings.mdFontColor;
|
||||
|
||||
let style = fileCache?.frontmatter
|
||||
? fileCache.frontmatter[FRONTMATTER_KEY_MD_STYLE] ?? ""
|
||||
: "";
|
||||
let frontmatterCSSisAfile = false;
|
||||
if (style && style != "") {
|
||||
const f = plugin.app.metadataCache.getFirstLinkpathDest(style, file.path);
|
||||
if (f) {
|
||||
style = await plugin.app.vault.read(f);
|
||||
frontmatterCSSisAfile = true;
|
||||
}
|
||||
}
|
||||
if (!frontmatterCSSisAfile) {
|
||||
if (plugin.settings.mdCSS && plugin.settings.mdCSS !== "") {
|
||||
const f = plugin.app.metadataCache.getFirstLinkpathDest(
|
||||
plugin.settings.mdCSS,
|
||||
file.path,
|
||||
);
|
||||
style += f ? `\n${await plugin.app.vault.read(f)}` : DEFAULT_MD_EMBED_CSS;
|
||||
} else {
|
||||
style += DEFAULT_MD_EMBED_CSS;
|
||||
}
|
||||
}
|
||||
|
||||
const borderColor = fileCache?.frontmatter
|
||||
? fileCache.frontmatter[FRONTMATTER_KEY_BORDERCOLOR] ??
|
||||
plugin.settings.mdBorderColor
|
||||
: plugin.settings.mdBorderColor;
|
||||
|
||||
if (borderColor && borderColor !== "" && !style.match(/svg/i)) {
|
||||
style += `svg{border:2px solid;color:${borderColor};transform:scale(.95)}`;
|
||||
}
|
||||
|
||||
//3.
|
||||
//SVG helper functions
|
||||
//the SVG will first have ~infinite height. After sizing this will be reduced
|
||||
let svgStyle = ` width="${linkParts.width}px" height="100000"`;
|
||||
let foreignObjectStyle = ` width="${linkParts.width}px" height="100%"`;
|
||||
|
||||
const svg = (xml: string, xmlFooter: string, style?: string) =>
|
||||
`<svg xmlns="http://www.w3.org/2000/svg"${svgStyle}>${
|
||||
style ? `<style>${style}</style>` : ""
|
||||
}<foreignObject x="0" y="0"${foreignObjectStyle}>${xml}${
|
||||
xmlFooter //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/286#issuecomment-982179639
|
||||
}</foreignObject>${
|
||||
fontDef !== "" ? `<defs><style>${fontDef}</style></defs>` : ""
|
||||
}</svg>`;
|
||||
|
||||
//4.
|
||||
//create document div - this will be the contents of the foreign object
|
||||
const mdDIV = createDiv();
|
||||
mdDIV.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
||||
mdDIV.setAttribute("class", "excalidraw-md-host");
|
||||
// mdDIV.setAttribute("style",style);
|
||||
if (fontName !== "") {
|
||||
mdDIV.style.fontFamily = fontName;
|
||||
}
|
||||
mdDIV.style.overflow = "auto";
|
||||
mdDIV.style.display = "block";
|
||||
mdDIV.style.color = fontColor && fontColor !== "" ? fontColor : "initial";
|
||||
|
||||
await MarkdownRenderer.renderMarkdown(text, mdDIV, file.path, plugin);
|
||||
mdDIV
|
||||
.querySelectorAll(":scope > *[class^='frontmatter']")
|
||||
.forEach((el) => mdDIV.removeChild(el));
|
||||
|
||||
//5.1
|
||||
//get SVG size.
|
||||
//First I need to create a fully self contained copy of the document to convert
|
||||
//blank styles into inline styles using computedStyle
|
||||
const iframeHost = document.body.createDiv();
|
||||
iframeHost.style.display = "none";
|
||||
const iframe = iframeHost.createEl("iframe");
|
||||
const iframeDoc = iframe.contentWindow.document;
|
||||
if (style) {
|
||||
const styleEl = iframeDoc.createElement("style");
|
||||
styleEl.type = "text/css";
|
||||
styleEl.innerHTML = style;
|
||||
iframeDoc.head.appendChild(styleEl);
|
||||
}
|
||||
const stylingDIV = iframeDoc.importNode(mdDIV, true);
|
||||
iframeDoc.body.appendChild(stylingDIV);
|
||||
const footerDIV = createDiv();
|
||||
footerDIV.setAttribute("class", "excalidraw-md-footer");
|
||||
iframeDoc.body.appendChild(footerDIV);
|
||||
|
||||
iframeDoc.body.querySelectorAll("*").forEach((el: HTMLElement) => {
|
||||
const elementStyle = el.style;
|
||||
const computedStyle = window.getComputedStyle(el);
|
||||
let style = "";
|
||||
for (const prop in elementStyle) {
|
||||
if (elementStyle.hasOwnProperty(prop)) {
|
||||
style += `${prop}: ${computedStyle[prop]};`;
|
||||
}
|
||||
}
|
||||
el.setAttribute("style", style);
|
||||
});
|
||||
|
||||
const xmlINiframe = new XMLSerializer().serializeToString(stylingDIV);
|
||||
const xmlFooter = new XMLSerializer().serializeToString(footerDIV);
|
||||
document.body.removeChild(iframeHost);
|
||||
|
||||
//5.2
|
||||
//get SVG size
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(
|
||||
svg(xmlINiframe, xmlFooter),
|
||||
"image/svg+xml",
|
||||
);
|
||||
const svgEl = doc.firstElementChild;
|
||||
const host = createDiv();
|
||||
host.appendChild(svgEl);
|
||||
document.body.appendChild(host);
|
||||
const footerHeight = svgEl.querySelector(
|
||||
".excalidraw-md-footer",
|
||||
).scrollHeight;
|
||||
const height =
|
||||
svgEl.querySelector(".excalidraw-md-host").scrollHeight + footerHeight;
|
||||
const svgHeight = height <= linkParts.height ? height : linkParts.height;
|
||||
document.body.removeChild(host);
|
||||
|
||||
//finalize SVG
|
||||
svgStyle = ` width="${linkParts.width}px" height="${svgHeight}px"`;
|
||||
foreignObjectStyle = ` width="${linkParts.width}px" height="${svgHeight}px"`;
|
||||
mdDIV.style.height = `${svgHeight - footerHeight}px`;
|
||||
mdDIV.style.overflow = "hidden";
|
||||
const xml = new XMLSerializer().serializeToString(mdDIV);
|
||||
const finalSVG = svg(xml, '<div class="excalidraw-md-footer"></div>', style);
|
||||
plugin.ea.mostRecentMarkdownSVG = parser.parseFromString(
|
||||
finalSVG,
|
||||
"image/svg+xml",
|
||||
).firstElementChild as SVGSVGElement;
|
||||
return svgToBase64(finalSVG) as DataURL;
|
||||
};
|
||||
|
||||
const generateIdFromFile = async (file: ArrayBuffer): Promise<FileId> => {
|
||||
let id: FileId;
|
||||
try {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,9 @@ import {
|
||||
FRONTMATTER_KEY_DEFAULT_MODE,
|
||||
fileid,
|
||||
REG_BLOCK_REF_CLEAN,
|
||||
FRONTMATTER_KEY_LINKBUTTON_OPACITY,
|
||||
FRONTMATTER_KEY_ONLOAD_SCRIPT,
|
||||
FRONTMATTER_KEY_AUTOEXPORT,
|
||||
} from "./Constants";
|
||||
import { _measureText } from "./ExcalidrawAutomate";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
@@ -49,6 +52,14 @@ declare module "obsidian" {
|
||||
}
|
||||
}
|
||||
|
||||
export enum AutoexportPreference {
|
||||
none,
|
||||
both,
|
||||
png,
|
||||
svg,
|
||||
inherit
|
||||
}
|
||||
|
||||
export const REGEX_LINK = {
|
||||
//![[link|alias]] [alias](link){num}
|
||||
// 1 2 3 4 5 67 8 9
|
||||
@@ -210,26 +221,29 @@ const wrap = (text: string, lineLen: number) =>
|
||||
lineLen ? wrapText(text, lineLen, false, 0) : text;
|
||||
|
||||
export class ExcalidrawData {
|
||||
private textElements: Map<
|
||||
public textElements: Map<
|
||||
string,
|
||||
{ raw: string; parsed: string; wrapAt: number | null }
|
||||
> = null;
|
||||
private elementLinks: Map<string, string> = null;
|
||||
public elementLinks: Map<string, string> = null;
|
||||
public scene: any = null;
|
||||
public deletedElements: ExcalidrawElement[] = [];
|
||||
private file: TFile = null;
|
||||
private app: App;
|
||||
private showLinkBrackets: boolean;
|
||||
private linkPrefix: string;
|
||||
private urlPrefix: string;
|
||||
public autoexportPreference: AutoexportPreference = AutoexportPreference.inherit;
|
||||
private textMode: TextMode = TextMode.raw;
|
||||
private plugin: ExcalidrawPlugin;
|
||||
public loaded: boolean = false;
|
||||
private files: Map<FileId, EmbeddedFile> = null; //fileId, path
|
||||
private equations: Map<FileId, { latex: string; isLoaded: boolean }> = null; //fileId, path
|
||||
private compatibilityMode: boolean = false;
|
||||
selectedElementIds: {[key:string]:boolean} = {}; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/609
|
||||
|
||||
constructor(plugin: ExcalidrawPlugin) {
|
||||
this.plugin = plugin;
|
||||
constructor(
|
||||
private plugin: ExcalidrawPlugin,
|
||||
) {
|
||||
this.app = plugin.app;
|
||||
this.files = new Map<FileId, EmbeddedFile>();
|
||||
this.equations = new Map<FileId, { latex: string; isLoaded: boolean }>();
|
||||
@@ -372,12 +386,13 @@ export class ExcalidrawData {
|
||||
public async loadData(
|
||||
data: string,
|
||||
file: TFile,
|
||||
textMode: TextMode,
|
||||
textMode: TextMode
|
||||
): Promise<boolean> {
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
this.loaded = false;
|
||||
this.selectedElementIds = {};
|
||||
this.textElements = new Map<
|
||||
string,
|
||||
{ raw: string; parsed: string; wrapAt: number }
|
||||
@@ -396,6 +411,7 @@ export class ExcalidrawData {
|
||||
this.setShowLinkBrackets();
|
||||
this.setLinkPrefix();
|
||||
this.setUrlPrefix();
|
||||
this.setAutoexportPreferences();
|
||||
|
||||
this.scene = null;
|
||||
|
||||
@@ -429,17 +445,10 @@ export class ExcalidrawData {
|
||||
}
|
||||
return sceneJSONandPOS;
|
||||
};
|
||||
//try {
|
||||
sceneJSONandPOS = loadJSON();
|
||||
/*} catch (e) {
|
||||
if(await this.app.vault.adapter.exists(getBakPath(file))) {
|
||||
data = await this.app.vault.adapter.read(getBakPath(file))
|
||||
sceneJSONandPOS = loadJSON();
|
||||
new Notice(t("LOAD_FROM_BACKUP"), 4000);
|
||||
} else {
|
||||
throw e;
|
||||
|
||||
}*/
|
||||
|
||||
this.deletedElements = this.scene.elements.filter((el:ExcalidrawElement)=>el.isDeleted);
|
||||
this.scene.elements = this.scene.elements.filter((el:ExcalidrawElement)=>!el.isDeleted);
|
||||
|
||||
if (!this.scene.files) {
|
||||
this.scene.files = {}; //loading legacy scenes that do not yet have the files attribute.
|
||||
@@ -550,6 +559,7 @@ export class ExcalidrawData {
|
||||
return false;
|
||||
}
|
||||
this.loaded = false;
|
||||
this.selectedElementIds = {};
|
||||
this.compatibilityMode = true;
|
||||
this.file = file;
|
||||
this.textElements = new Map<
|
||||
@@ -636,23 +646,23 @@ export class ExcalidrawData {
|
||||
id: string,
|
||||
wrapResult: boolean = true,
|
||||
): Promise<string> {
|
||||
const t = this.textElements.get(id);
|
||||
if (!t) {
|
||||
const text = this.textElements.get(id);
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
if (this.textMode === TextMode.parsed) {
|
||||
if (!t.parsed) {
|
||||
if (!text.parsed) {
|
||||
this.textElements.set(id, {
|
||||
raw: t.raw,
|
||||
parsed: (await this.parse(t.raw)).parsed,
|
||||
wrapAt: t.wrapAt,
|
||||
raw: text.raw,
|
||||
parsed: (await this.parse(text.raw)).parsed,
|
||||
wrapAt: text.wrapAt,
|
||||
});
|
||||
}
|
||||
//console.log("parsed",this.textElements.get(id).parsed);
|
||||
return wrapResult ? wrap(t.parsed, t.wrapAt) : t.parsed;
|
||||
return wrapResult ? wrap(text.parsed, text.wrapAt) : text.parsed;
|
||||
}
|
||||
//console.log("raw",this.textElements.get(id).raw);
|
||||
return t.raw;
|
||||
return text.raw;
|
||||
}
|
||||
|
||||
private findNewElementLinksInScene(): boolean {
|
||||
@@ -690,9 +700,10 @@ export class ExcalidrawData {
|
||||
* check for textElements in Scene missing from textElements Map
|
||||
* @returns {boolean} - true if there were changes
|
||||
*/
|
||||
private findNewTextElementsInScene(): boolean {
|
||||
private findNewTextElementsInScene(selectedElementIds: {[key: string]: boolean} = {}): boolean {
|
||||
//console.log("Excalidraw.Data.findNewTextElementsInScene()");
|
||||
//get scene text elements
|
||||
this.selectedElementIds = selectedElementIds;
|
||||
const texts = this.scene.elements?.filter((el: any) => el.type === "text");
|
||||
|
||||
let jsonString = JSON.stringify(this.scene);
|
||||
@@ -707,14 +718,18 @@ export class ExcalidrawData {
|
||||
if (te.id.length > 8) {
|
||||
dirty = true;
|
||||
id = nanoid();
|
||||
if(this.selectedElementIds[te.id]) { //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/609
|
||||
delete this.selectedElementIds[te.id];
|
||||
this.selectedElementIds[id] = true;
|
||||
}
|
||||
jsonString = jsonString.replaceAll(te.id, id); //brute force approach to replace all occurances (e.g. links, groups,etc.)
|
||||
if (this.textElements.has(te.id)) {
|
||||
//element was created with onBeforeTextSubmit
|
||||
const t = this.textElements.get(te.id);
|
||||
const text = this.textElements.get(te.id);
|
||||
this.textElements.set(id, {
|
||||
raw: t.raw,
|
||||
parsed: t.parsed,
|
||||
wrapAt: t.wrapAt,
|
||||
raw: text.raw,
|
||||
parsed: text.parsed,
|
||||
wrapAt: text.wrapAt,
|
||||
});
|
||||
this.textElements.delete(te.id); //delete the old ID from the Map
|
||||
}
|
||||
@@ -952,7 +967,7 @@ export class ExcalidrawData {
|
||||
* @returns markdown string
|
||||
*/
|
||||
disableCompression: boolean = false;
|
||||
generateMD(): string {
|
||||
generateMD(deletedElements: ExcalidrawElement[] = []): string {
|
||||
let outString = "# Text Elements\n";
|
||||
for (const key of this.textElements.keys()) {
|
||||
outString += `${this.textElements.get(key).raw} ^${key}\n\n`;
|
||||
@@ -978,7 +993,14 @@ export class ExcalidrawData {
|
||||
}
|
||||
outString += this.equations.size > 0 || this.files.size > 0 ? "\n" : "";
|
||||
|
||||
const sceneJSONstring = JSON.stringify(this.scene, null, "\t");
|
||||
const sceneJSONstring = JSON.stringify({
|
||||
type: this.scene.type,
|
||||
version: this.scene.version,
|
||||
source: this.scene.source,
|
||||
elements: this.scene.elements.concat(deletedElements),
|
||||
appState: this.scene.appState,
|
||||
files: this.scene.files
|
||||
}, null, "\t");
|
||||
return (
|
||||
outString +
|
||||
getMarkdownDrawingSection(
|
||||
@@ -1021,6 +1043,39 @@ export class ExcalidrawData {
|
||||
return false;
|
||||
}
|
||||
|
||||
//assing new fileId to duplicate equation and markdown files
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/601
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/593
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/297
|
||||
const processedIds = new Set<string>();
|
||||
fileIds.forEach(fileId=>{
|
||||
if(processedIds.has(fileId)) {
|
||||
const file = this.getFile(fileId);
|
||||
//const file = this.files.get(fileId as FileId);
|
||||
const equation = this.getEquation(fileId);
|
||||
//const equation = this.equations.get(fileId as FileId);
|
||||
//images should have a single reference, but equations and markdown embeds should have as many as instances of the file in the scene
|
||||
if(file && (file.file.extension !== "md" || this.plugin.isExcalidrawFile(file.file))) {
|
||||
return;
|
||||
}
|
||||
const newId = fileid();
|
||||
//scene.files[newId] = {...scene.files[fileId]};
|
||||
(scene.elements.filter((el:ExcalidrawImageElement)=>el.fileId === fileId)[0] as any).fileId = newId;
|
||||
dirty = true;
|
||||
processedIds.add(newId);
|
||||
if(file) {
|
||||
this.setFile(newId as FileId,new EmbeddedFile(this.plugin,this.file.path,file.linkParts.original));
|
||||
//this.files.set(newId as FileId,new EmbeddedFile(this.plugin,this.file.path,file.linkParts.original))
|
||||
}
|
||||
if(equation) {
|
||||
this.setEquation(newId as FileId, {latex:equation.latex, isLoaded:false});
|
||||
//this.equations.set(newId as FileId, equation);
|
||||
}
|
||||
}
|
||||
processedIds.add(fileId);
|
||||
});
|
||||
|
||||
|
||||
for (const key of Object.keys(scene.files)) {
|
||||
if (!(this.hasFile(key as FileId) || this.hasEquation(key as FileId))) {
|
||||
dirty = true;
|
||||
@@ -1069,7 +1124,7 @@ export class ExcalidrawData {
|
||||
}
|
||||
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/297
|
||||
const equations = new Set<string>();
|
||||
/*const equations = new Set<string>();
|
||||
const duplicateEqs = new Set<string>();
|
||||
for (const key of fileIds) {
|
||||
if (this.hasEquation(key as FileId)) {
|
||||
@@ -1095,12 +1150,12 @@ export class ExcalidrawData {
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
public async syncElements(newScene: any): Promise<boolean> {
|
||||
public async syncElements(newScene: any, selectedElementIds?: {[key: string]: boolean}): Promise<boolean> {
|
||||
this.scene = newScene;
|
||||
let result = false;
|
||||
if (!this.compatibilityMode) {
|
||||
@@ -1115,7 +1170,7 @@ export class ExcalidrawData {
|
||||
this.setShowLinkBrackets() ||
|
||||
this.findNewElementLinksInScene();
|
||||
await this.updateTextElementsFromScene();
|
||||
return result || this.findNewTextElementsInScene();
|
||||
return result || this.findNewTextElementsInScene(selectedElementIds);
|
||||
}
|
||||
|
||||
public async updateScene(newScene: any) {
|
||||
@@ -1225,6 +1280,29 @@ export class ExcalidrawData {
|
||||
}
|
||||
}
|
||||
|
||||
public getLinkOpacity(): number {
|
||||
const fileCache = this.app.metadataCache.getFileCache(this.file);
|
||||
let opacity = this.plugin.settings.linkOpacity;
|
||||
if (
|
||||
fileCache?.frontmatter &&
|
||||
fileCache.frontmatter[FRONTMATTER_KEY_LINKBUTTON_OPACITY] != null
|
||||
) {
|
||||
opacity = fileCache.frontmatter[FRONTMATTER_KEY_LINKBUTTON_OPACITY];
|
||||
}
|
||||
return opacity;
|
||||
}
|
||||
|
||||
public getOnLoadScript(): string {
|
||||
const fileCache = this.app.metadataCache.getFileCache(this.file);
|
||||
if (
|
||||
fileCache?.frontmatter &&
|
||||
fileCache.frontmatter[FRONTMATTER_KEY_ONLOAD_SCRIPT] != null
|
||||
) {
|
||||
return fileCache.frontmatter[FRONTMATTER_KEY_ONLOAD_SCRIPT];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private setLinkPrefix(): boolean {
|
||||
const linkPrefix = this.linkPrefix;
|
||||
const fileCache = this.app.metadataCache.getFileCache(this.file);
|
||||
@@ -1253,6 +1331,24 @@ export class ExcalidrawData {
|
||||
return urlPrefix != this.urlPrefix;
|
||||
}
|
||||
|
||||
private setAutoexportPreferences() {
|
||||
const fileCache = this.app.metadataCache.getFileCache(this.file);
|
||||
if (
|
||||
fileCache?.frontmatter &&
|
||||
fileCache.frontmatter[FRONTMATTER_KEY_AUTOEXPORT] != null
|
||||
) {
|
||||
switch ((fileCache.frontmatter[FRONTMATTER_KEY_AUTOEXPORT]).toLowerCase()) {
|
||||
case "none": this.autoexportPreference = AutoexportPreference.none; break;
|
||||
case "both": this.autoexportPreference = AutoexportPreference.both; break;
|
||||
case "png": this.autoexportPreference = AutoexportPreference.png; break;
|
||||
case "svg": this.autoexportPreference = AutoexportPreference.svg; break;
|
||||
default: this.autoexportPreference = AutoexportPreference.inherit;
|
||||
};
|
||||
} else {
|
||||
this.autoexportPreference = AutoexportPreference.inherit;
|
||||
}
|
||||
}
|
||||
|
||||
private setShowLinkBrackets(): boolean {
|
||||
const showLinkBrackets = this.showLinkBrackets;
|
||||
const fileCache = this.app.metadataCache.getFileCache(this.file);
|
||||
@@ -1287,14 +1383,35 @@ export class ExcalidrawData {
|
||||
if (!data.file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parts = data.linkParts.original.split("#");
|
||||
this.plugin.filesMaster.set(fileId, {
|
||||
path: data.file.path,
|
||||
path:data.file.path,
|
||||
blockrefData: parts.length === 1
|
||||
? null
|
||||
: parts[1],
|
||||
hasSVGwithBitmap: data.isSVGwithBitmap,
|
||||
});
|
||||
}
|
||||
|
||||
public getFiles(): EmbeddedFile[] {
|
||||
return Object.values(this.files);
|
||||
}
|
||||
|
||||
public getFile(fileId: FileId): EmbeddedFile {
|
||||
return this.files.get(fileId);
|
||||
let embeddedFile = this.files.get(fileId);
|
||||
if(embeddedFile) return embeddedFile;
|
||||
const masterFile = this.plugin.filesMaster.get(fileId);
|
||||
if(!masterFile) return embeddedFile;
|
||||
embeddedFile = new EmbeddedFile(
|
||||
this.plugin,
|
||||
this.file.path,
|
||||
masterFile.blockrefData
|
||||
? masterFile.path + "#" + masterFile.blockrefData
|
||||
: masterFile.path
|
||||
);
|
||||
this.files.set(fileId,embeddedFile);
|
||||
return embeddedFile;
|
||||
}
|
||||
|
||||
public getFileEntries() {
|
||||
@@ -1313,15 +1430,17 @@ export class ExcalidrawData {
|
||||
return true;
|
||||
}
|
||||
if (this.plugin.filesMaster.has(fileId)) {
|
||||
const fileMaster = this.plugin.filesMaster.get(fileId);
|
||||
if (!this.app.vault.getAbstractFileByPath(fileMaster.path)) {
|
||||
const masterFile = this.plugin.filesMaster.get(fileId);
|
||||
if (!this.app.vault.getAbstractFileByPath(masterFile.path)) {
|
||||
this.plugin.filesMaster.delete(fileId);
|
||||
return true;
|
||||
} // the file no longer exists
|
||||
const embeddedFile = new EmbeddedFile(
|
||||
this.plugin,
|
||||
this.file.path,
|
||||
fileMaster.path,
|
||||
masterFile.blockrefData
|
||||
? masterFile.path + "#" + masterFile.blockrefData
|
||||
: masterFile.path
|
||||
);
|
||||
this.files.set(fileId, embeddedFile);
|
||||
return true;
|
||||
@@ -1338,7 +1457,12 @@ export class ExcalidrawData {
|
||||
}
|
||||
|
||||
public getEquation(fileId: FileId): { latex: string; isLoaded: boolean } {
|
||||
return this.equations.get(fileId);
|
||||
let result = this.equations.get(fileId);
|
||||
if(result) return result;
|
||||
const latex = this.plugin.equationsMaster.get(fileId);
|
||||
if(!latex) return result;
|
||||
this.equations.set(fileId, {latex, isLoaded: false});
|
||||
return {latex, isLoaded: false};
|
||||
}
|
||||
|
||||
public getEquationEntries() {
|
||||
@@ -1372,7 +1496,7 @@ export const getTransclusion = async (
|
||||
app: App,
|
||||
file: TFile,
|
||||
charCountLimit?: number,
|
||||
): Promise<{ contents: string; lineNum: number }> => {
|
||||
): Promise<{ contents: string; lineNum: number; leadingHashes?: string; }> => {
|
||||
//file-name#^blockref
|
||||
//1 2 3
|
||||
|
||||
@@ -1425,10 +1549,21 @@ export const getTransclusion = async (
|
||||
let startPos: number = null;
|
||||
let lineNum: number = 0;
|
||||
let endPos: number = null;
|
||||
let depth:number = 1;
|
||||
for (let i = 0; i < headings.length; i++) {
|
||||
if (startPos && !endPos) {
|
||||
endPos = headings[i].node.position.start.offset - 1;
|
||||
let j = i;
|
||||
while (j<headings.length && headings[j].node.depth>depth) {j++};
|
||||
if(j === headings.length && headings[j-1].node.depth > depth) {
|
||||
return {
|
||||
leadingHashes: "#".repeat(depth)+" ",
|
||||
contents: contents.substring(startPos).trim(),
|
||||
lineNum
|
||||
};
|
||||
}
|
||||
endPos = headings[j].node.position.start.offset - 1;
|
||||
return {
|
||||
leadingHashes: "#".repeat(depth)+" ",
|
||||
contents: contents.substring(startPos, endPos).trim(),
|
||||
lineNum,
|
||||
};
|
||||
@@ -1436,6 +1571,7 @@ export const getTransclusion = async (
|
||||
const c = headings[i].node.children[0];
|
||||
const dataHeading = headings[i].node.data?.hProperties?.dataHeading;
|
||||
const cc = c?.children;
|
||||
//const refNoSpace = linkParts.ref.replaceAll(" ","");
|
||||
if (
|
||||
!startPos &&
|
||||
(c?.value?.replaceAll(REG_BLOCK_REF_CLEAN, "") === linkParts.ref ||
|
||||
@@ -1446,11 +1582,16 @@ export const getTransclusion = async (
|
||||
: false))
|
||||
) {
|
||||
startPos = headings[i].node.children[0]?.position.start.offset; //
|
||||
depth = headings[i].node.depth;
|
||||
lineNum = headings[i].node.children[0]?.position.start.line; //
|
||||
}
|
||||
}
|
||||
if (startPos) {
|
||||
return { contents: contents.substring(startPos).trim(), lineNum };
|
||||
return {
|
||||
leadingHashes: "#".repeat(depth) + " ",
|
||||
contents: contents.substring(startPos).trim(),
|
||||
lineNum
|
||||
};
|
||||
}
|
||||
return { contents: linkParts.original.trim(), lineNum: 0 };
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
10
src/LaTeX.ts
10
src/LaTeX.ts
@@ -3,10 +3,9 @@ import ExcalidrawView from "./ExcalidrawView";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
import { FileData, MimeType } from "./EmbeddedFileLoader";
|
||||
import { FileId } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { getImageSize, log, sleep, svgToBase64 } from "./utils/Utils";
|
||||
import { errorlog, getImageSize, log, sleep, svgToBase64 } from "./utils/Utils";
|
||||
import { fileid } from "./Constants";
|
||||
import html2canvas from "html2canvas";
|
||||
import { count } from "console";
|
||||
import { Notice } from "obsidian";
|
||||
|
||||
declare let window: any;
|
||||
@@ -46,11 +45,14 @@ export async function tex2dataURL(
|
||||
//if network is slow, or not available, or mathjax has not yet fully loaded
|
||||
let counter = 0;
|
||||
while (!plugin.mathjax && !plugin.mathjaxLoaderFinished && counter < 10) {
|
||||
log({ where: "tex2dataURL", counter });
|
||||
await sleep(100);
|
||||
counter++;
|
||||
}
|
||||
|
||||
if(!plugin.mathjaxLoaderFinished) {
|
||||
errorlog({where: "text2dataURL", fn: tex2dataURL, message:"mathjaxLoader not ready, using fallback. Try reloading Obsidian or restarting the Excalidraw plugin"});
|
||||
}
|
||||
|
||||
//it is not clear why this works, but it seems that after loading the plugin sometimes only the third attempt is successful.
|
||||
try {
|
||||
return await mathjaxSVG(tex, plugin);
|
||||
@@ -79,7 +81,7 @@ export async function tex2dataURL(
|
||||
}
|
||||
}
|
||||
|
||||
async function mathjaxSVG(
|
||||
export async function mathjaxSVG(
|
||||
tex: string,
|
||||
plugin: ExcalidrawPlugin,
|
||||
): Promise<{
|
||||
|
||||
@@ -36,7 +36,7 @@ let metadataCache: MetadataCache;
|
||||
|
||||
const getDefaultWidth = (plugin: ExcalidrawPlugin): string => {
|
||||
const width = parseInt(plugin.settings.width);
|
||||
if (isNaN(width) || width === 0) {
|
||||
if (isNaN(width) || width === 0 || width === null) {
|
||||
return "400";
|
||||
}
|
||||
return plugin.settings.width;
|
||||
@@ -129,6 +129,7 @@ const getIMG = async (
|
||||
null,
|
||||
[],
|
||||
plugin,
|
||||
0
|
||||
));
|
||||
if (!png) {
|
||||
return null;
|
||||
@@ -152,6 +153,7 @@ const getIMG = async (
|
||||
null,
|
||||
[],
|
||||
plugin,
|
||||
0,
|
||||
getSVGPadding(plugin, file),
|
||||
)
|
||||
).outerHTML;
|
||||
@@ -196,7 +198,11 @@ const createImageDiv = async (
|
||||
if (src) {
|
||||
plugin.openDrawing(
|
||||
vault.getAbstractFileByPath(src) as TFile,
|
||||
ev[CTRL_OR_CMD],
|
||||
ev[CTRL_OR_CMD]
|
||||
? "new-pane"
|
||||
: (ev.metaKey && !app.isMobile)
|
||||
? "popout-window"
|
||||
: "active-pane",
|
||||
);
|
||||
} //.ctrlKey||ev.metaKey);
|
||||
});
|
||||
@@ -339,8 +345,8 @@ const tmpObsidianWYSIWYG = async (
|
||||
|
||||
const basename = splitFolderAndFilename(attr.fname).basename;
|
||||
const setAttr = () => {
|
||||
const hasWidth = internalEmbedDiv.getAttribute("width") !== "";
|
||||
const hasHeight = internalEmbedDiv.getAttribute("height") !== "";
|
||||
const hasWidth = internalEmbedDiv.getAttribute("width") && (internalEmbedDiv.getAttribute("width") !== "");
|
||||
const hasHeight = internalEmbedDiv.getAttribute("height") && (internalEmbedDiv.getAttribute("height") !== "");
|
||||
if (hasWidth) {
|
||||
attr.fwidth = internalEmbedDiv.getAttribute("width");
|
||||
}
|
||||
@@ -512,7 +518,11 @@ export const observer = new MutationObserver(async (m) => {
|
||||
if (src) {
|
||||
plugin.openDrawing(
|
||||
vault.getAbstractFileByPath(src) as TFile,
|
||||
ev[CTRL_OR_CMD],
|
||||
ev[CTRL_OR_CMD]
|
||||
? "new-pane"
|
||||
: (ev.metaKey && !app.isMobile)
|
||||
? "popout-window"
|
||||
: "active-pane",
|
||||
);
|
||||
} //.ctrlKey||ev.metaKey);
|
||||
});
|
||||
|
||||
@@ -163,14 +163,16 @@ export class ScriptEngine {
|
||||
name: `(Script) ${scriptName}`,
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return (
|
||||
this.plugin.app.workspace.activeLeaf.view.getViewType() ==
|
||||
VIEW_TYPE_EXCALIDRAW
|
||||
);
|
||||
return Boolean(app.workspace.getActiveViewOfType(ExcalidrawView));
|
||||
}
|
||||
const view = this.plugin.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
this.executeScript(view, f);
|
||||
const view = app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
(async()=>{
|
||||
const script = await this.plugin.app.vault.read(f);
|
||||
if(script) {
|
||||
this.executeScript(view, script, scriptName);
|
||||
}
|
||||
})()
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -206,18 +208,13 @@ export class ScriptEngine {
|
||||
delete app.commands.commands[commandId];
|
||||
}
|
||||
|
||||
async executeScript(view: ExcalidrawView, f: TFile) {
|
||||
if (!view || !f) {
|
||||
async executeScript(view: ExcalidrawView, script: string, title: string) {
|
||||
if (!view || !script || !title) {
|
||||
return;
|
||||
}
|
||||
this.plugin.ea.reset();
|
||||
this.plugin.ea.setView(view);
|
||||
const script = await this.plugin.app.vault.read(f);
|
||||
if (!script) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.plugin.ea.activeScript = this.getScriptName(f);
|
||||
this.plugin.ea.activeScript = title;
|
||||
|
||||
//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
|
||||
|
||||
@@ -18,7 +18,8 @@ export const SCRIPT_INSTALL_FOLDER = "Downloaded";
|
||||
export const fileid = customAlphabet("1234567890abcdef", 40);
|
||||
export const REG_LINKINDEX_INVALIDCHARS = /[<>:"\\|?*#]/g;
|
||||
export const REG_BLOCK_REF_CLEAN =
|
||||
/\+|\/|~|=|%|\(|\)|{|}|,|\.|\$|!|\?|;|\[|]|\^|#|\*|<|>|&|@|\||\\|"|:/g;
|
||||
/[!"#$%&()*+,.:;<=>?@^`{|}~\/\[\]\\]/g; //https://discord.com/channels/686053708261228577/989603365606531104/1000128926619816048
|
||||
// /\+|\/|~|=|%|\(|\)|{|}|,|&|\.|\$|!|\?|;|\[|]|\^|#|\*|<|>|&|@|\||\\|"|:|\s/g;
|
||||
export const IMAGE_TYPES = ["jpeg", "jpg", "png", "gif", "svg"];
|
||||
export const MAX_IMAGE_SIZE = 500;
|
||||
export const FRONTMATTER_KEY = "excalidraw-plugin";
|
||||
@@ -30,11 +31,14 @@ export const FRONTMATTER_KEY_EXPORT_PNGSCALE = "excalidraw-export-pngscale";
|
||||
export const FRONTMATTER_KEY_CUSTOM_PREFIX = "excalidraw-link-prefix";
|
||||
export const FRONTMATTER_KEY_CUSTOM_URL_PREFIX = "excalidraw-url-prefix";
|
||||
export const FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS = "excalidraw-link-brackets";
|
||||
export const FRONTMATTER_KEY_ONLOAD_SCRIPT = "excalidraw-onload-script";
|
||||
export const FRONTMATTER_KEY_LINKBUTTON_OPACITY = "excalidraw-linkbutton-opacity";
|
||||
export const FRONTMATTER_KEY_DEFAULT_MODE = "excalidraw-default-mode";
|
||||
export const FRONTMATTER_KEY_FONT = "excalidraw-font";
|
||||
export const FRONTMATTER_KEY_FONTCOLOR = "excalidraw-font-color";
|
||||
export const FRONTMATTER_KEY_BORDERCOLOR = "excalidraw-border-color";
|
||||
export const FRONTMATTER_KEY_MD_STYLE = "excalidraw-css";
|
||||
export const FRONTMATTER_KEY_AUTOEXPORT = "excalidraw-autoexport"
|
||||
export const LOCAL_PROTOCOL = "md://";
|
||||
export const VIEW_TYPE_EXCALIDRAW = "excalidraw";
|
||||
export const ICON_NAME = "excalidraw-icon";
|
||||
|
||||
@@ -16,6 +16,240 @@ export const RELEASE_NOTES: { [k: string]: string } = {
|
||||
I develop this plugin as a hobby, spending most of my free time doing this. If you'd like to contribute to the on-going work, I have a simple membership scheme with Bronze, Silver and Gold tiers. Many of you have already bought me a coffee. THANK YOU! It really means a lot to me! If you find this plugin valuable, please consider supporting me.
|
||||
|
||||
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://cdn.ko-fi.com/cdn/kofi3.png?v=3" height=45></a></div>
|
||||
`,
|
||||
"1.7.12": `
|
||||
# New from Excalidraw.com:
|
||||
- Showing a mid-point for lines and arrows. By touching the mid-point you can easily add an additional point to a two-point line. This is especially helpful when working on a tablet with touch input. ([#5534](https://github.com/excalidraw/excalidraw/pull/5534))
|
||||
- Lock angle when editing a line or an arrow with SHIFT pressed. Pressing SHIFT will restrict the edited point to snap to certain discrete angles. ([#5527](https://github.com/excalidraw/excalidraw/pull/5527))
|
||||
|
||||
# Fixed:
|
||||
- Clicking Obsidian search-results pointing to an element on the canvas works again ([#734](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/734))
|
||||
- The feature to allow resizing and rotation of lines and arrows consisting of 3 or more points by showing the bounding box when selected is back ([#5554](https://github.com/excalidraw/excalidraw/pull/5554))
|
||||
|
||||
# New
|
||||
- You can now use the following frontmatter key to allow/prevent automatic export of PNG/SVG images at a file level. This frontmatter will override export settings for the given file. ([#732](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/732)
|
||||
${String.fromCharCode(96)}excalidraw-autoexport: none|both|svg|png${String.fromCharCode(96)}
|
||||
`,
|
||||
"1.7.11": `
|
||||
# Fixed
|
||||
- Markdown files embed into the Excalidraw canvas crashed when the embedded markdown file included a nested Markdown embed with a block reference (i.e. the markdown document you are dropping into Excalidraw included a quote you referenced from another file using a ${String.fromCharCode(96)}[[other-file#^blockref]]${String.fromCharCode(96)} block or section reference.
|
||||
- Horizontal flipping of arrows and lines broke in 1.7.10. ([#726](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/726))
|
||||
`,
|
||||
"1.7.10": `
|
||||
# New from Excalidraw.com
|
||||
- Improved handling of arrows and lines. ([#5501](https://github.com/excalidraw/excalidraw/pull/5501))
|
||||
|
||||
# Fixed
|
||||
- When opening a document in view-mode or zen-mode the panel buttons no longer flash up for a moment before switching to the desired mode. ([#479](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/479))
|
||||
- The "blinding white screen" no longer flashes up while loading the scene if the scene is dark ([#241](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/241))
|
||||
|
||||
# Under the hood
|
||||
- Finalized migration to React 18 (no longer showing an error about React 17 compatibility mode in console log)
|
||||
`,
|
||||
"1.7.9": `
|
||||
# New features and fixes from Excalidraw.com:
|
||||
- The right-click context menu is now scrollable on smaller screens ([#4030](https://github.com/excalidraw/excalidraw/pull/4030), [#5520](https://github.com/excalidraw/excalidraw/pull/5520))
|
||||
- Holding down the shift key while rotating an object will rotate it at discrete angles. Rotation is continuous without the SHIFT key. ([#5500](https://github.com/excalidraw/excalidraw/pull/5500))
|
||||
- Improved cursor alignment when resizing an element proportionally (maintain aspect ratio) by holding SHIFT during resizing. ([#5513](https://github.com/excalidraw/excalidraw/pull/5515))
|
||||
- Improved freedraw performance during editing (now has proper canvas caching), and no more blurry freedraw shapes when exporting on a higher scale. ([#5481](https://github.com/excalidraw/excalidraw/pull/5481))
|
||||
- Sidebar stencil library now correctly scrolls vertically ([#5459](https://github.com/excalidraw/excalidraw/pull/5459))
|
||||
|
||||
# New in Obsidian:
|
||||
- Fullscreen mode on iPad. When there are multiple work panes open, clicking the fullscreen action in the Excalidraw Obsidian menu will hide the other work panes and make Excalidraw fullscreen.
|
||||
|
||||
# Fixes in Obsidian:
|
||||
- Drag&Drop an image from a web browser into Excalidraw ([#697](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/697))
|
||||
- On Obsidian Mobile 1.3.0, when the drawing included an embedded image, switching from markdown-view to Excalidraw-view caused the drawing to disappear (it had to be recovered from backup or synchronization history). ([#715](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/715))
|
||||
- When working on a mobile device (tablet and phone) and using two work panes (one for drawing and the other for editing a markdown document) if you switched focus from the drawing to the markdown document auto-zoom changed the zoom level of the drawing. ([#723](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/723)), ([#705](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/705))
|
||||
- Actions on the Command Palette to create a new drawing in a new pane or reusing an existing adjacent pane; on the main workspace or in the Hover Editor or Popout window, were not working well. See related settings in plugin settings under "Links and transclusions" ([#718](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/718))
|
||||
- There was a problem with links with section references when the header contained space characters ([#704](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/704))
|
||||
- I added additional controls to avoid the fantom warnings about a problem with saving the Excalidraw file. Hopefully, from now on, you'll see this error less frequently ([#701](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/701))
|
||||
`,
|
||||
"1.7.8": `
|
||||
# Optimized for Obsidian 0.15.5
|
||||
- I reworked how the plugin treats the "More options" menu because the old approach was interfering with Obsidian
|
||||
- Did thorough testing of handling of work panes on link click. There are two settings (open in the adjacent pane, and open in the main workspace), and three broad scenarios (Excalidraw in a work pane in the main Obsidian window, Excalidraw in a hover editor, and Excalidraw in an Obsidian popout window). All should work correctly now.
|
||||
`,
|
||||
"1.7.7": `
|
||||
# New
|
||||
- Optimized for Obsidian 0.15.4
|
||||
- On a desktop, you can now use the META key when clicking on a link and it will open the link in a new popout Window.
|
||||
- ([#685](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/685)) Markdown embeds will now display correctly in Excalidraw even if they include photos and recursive markdown embeds. Unfortunately due to the limitations of Safari the inversion of colors on iPads in dark mode will not work well.
|
||||
See an 18 second long demo video [here](https://user-images.githubusercontent.com/14358394/177213263-2a7ef1ca-0614-4190-8955-e830ca6b424b.mp4).
|
||||
|
||||
|
||||
# Fixed
|
||||
- ([#683](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/683)) Copy/Paste Markdown embeds to create another instance of the embed, thus you can reference different sections of the document in your drawing (something I broke in 1.7.6)
|
||||
- ([#684](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/684)) Transclusions incorrectly did not pick up subsections of a section. To understand this change, imagine for example the following document:
|
||||
${String.fromCharCode(96, 96, 96)}markdown
|
||||
# A
|
||||
abc
|
||||
# B
|
||||
xyz
|
||||
## b1
|
||||
123
|
||||
## b2
|
||||
456
|
||||
# C
|
||||
${String.fromCharCode(96, 96, 96)}
|
||||
When you transclude ${String.fromCharCode(96)}![[document#B]]${String.fromCharCode(96)} you expect the following result
|
||||
${String.fromCharCode(96, 96, 96)}markdown
|
||||
B
|
||||
xyz
|
||||
|
||||
b1
|
||||
123
|
||||
|
||||
b2
|
||||
456
|
||||
${String.fromCharCode(96, 96, 96)}
|
||||
Until this fix you only got
|
||||
${String.fromCharCode(96, 96, 96)}markdown
|
||||
B
|
||||
xyz
|
||||
${String.fromCharCode(96, 96, 96)}`,
|
||||
"1.7.6": `
|
||||
This release is the same as 1.7.5 except for two minor fixes
|
||||
- a fix for ExcaliBrain, becuase 1.7.5 broke ExcaliBrain.
|
||||
- I left out the release note from 1.7.5.
|
||||
|
||||
# New
|
||||
- Deployed sidebar for libraries panel from excalidraw.com ([#5274](https://github.com/excalidraw/excalidraw/pull/5274)). You can dock the library to the right side depending on the screen real estate available (i.e. does not work on mobiles).
|
||||
|
||||
# Fixed
|
||||
- When copying 2 identical images from one drawing to another, the second image got corrupted in the process ([#672]https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/672)).
|
||||
- When making a copy of an equation in a drawing and then without first closing/opening the file, immediately copying the new equation to another drawing, the equation did not get displayed until the file was closed and reopened.
|
||||
- Copying a markdown embed from one drawing to another, in the destination the markdown embed appeared without the section/block reference and without the width & height (i.e. these settings had to be done again)
|
||||
- Improved the parsing of section references in embeds. When you had ${String.fromCharCode(96)}&${String.fromCharCode(96)} in the section name in a markdown file, when embedding that markdown document into Excalidraw, the section reference did not work as expected ([#681 ](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/681)).
|
||||
- Improved the logic for autosave to better detect changes to the document, and to reduce too frequent export of ${String.fromCharCode(96)}.png${String.fromCharCode(96)} and/or ${String.fromCharCode(96)}.svg${String.fromCharCode(96)} files, when auto export is enabled in plugin settings.
|
||||
`,
|
||||
"1.7.5": `
|
||||
# New
|
||||
- Deployed sidebar for libraries panel from excalidraw.com ([#5274](https://github.com/excalidraw/excalidraw/pull/5274)). You can dock the library to the right side depending on the screen real estate available (i.e. does not work on mobiles).
|
||||
|
||||
# Fixed
|
||||
- When copying 2 identical images from one drawing to another, the second image got corrupted in the process ([#672]https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/672)).
|
||||
- When making a copy of an equation in a drawing and then without first closing/opening the file, immediately copying the new equation to another drawing, the equation did not get displayed until the file was closed and reopened.
|
||||
- Copying a markdown embed from one drawing to another, in the destination the markdown embed appeared without the section/block reference and without the width & height (i.e. these settings had to be done again)
|
||||
- Improved the parsing of section references in embeds. When you had ${String.fromCharCode(96)}&${String.fromCharCode(96)} in the section name in a markdown file, when embedding that markdown document into Excalidraw, the section reference did not work as expected ([#681 ](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/681)).
|
||||
- Improved the logic for autosave to better detect changes to the document, and to reduce too frequent export of ${String.fromCharCode(96)}.png${String.fromCharCode(96)} and/or ${String.fromCharCode(96)}.svg${String.fromCharCode(96)} files, when auto export is enabled in plugin settings.
|
||||
`,
|
||||
"1.7.4": `
|
||||
- Obsidian 0.15.3 support dragging and dropping work panes between Obsidian windows.
|
||||
- Addressed Obsidian changes affecting the more-options menu.
|
||||
- Addressed incompatibility with Obsidian Mobile 1.2.2.
|
||||
`,
|
||||
"1.7.3": `
|
||||
Obsidian 0.15.3 support for dragging and dropping work panes between Obsidian windows.
|
||||
`,
|
||||
"1.7.2": `
|
||||
Due to some of the changes to the code, I highly recommend restarting Obsidian after installing this update to Excalidraw.
|
||||
|
||||
# Fixed
|
||||
- Stability improvements
|
||||
- Opening links in new panes and creating new drawings from the file explorer works properly again
|
||||
|
||||
# New feature
|
||||
- Two new command palette actions:
|
||||
- Create a new drawing - IN A POPOUT WINDOW
|
||||
- Create a new drawing - IN A POPOUT WINDOW - and embed into active document
|
||||

|
||||
- Added setting to prefer opening the link in the popout window or in the main workspace.
|
||||

|
||||
`,
|
||||
"1.7.1": `
|
||||
Support for Obsidian 0.15.0 popout windows. While there are no new features (apart from the popout window support) under the hood there were some major changes required to make this happen.
|
||||
`,
|
||||
"1.7.0": `
|
||||
This is the first test version of Excalidraw Obsidian supporting Obsidian 0.15.0 popout windows. The current technical solution is not really sustainable, it's more of a working concept. I don't expect any real big issues with this version - on the contrary, this works much better with Obsidian 0.15.0 popout windows, but some of the features aren't working as expected in the Obsidian popouts yet. Also as a consequence of Obsidian 0.15.0 compatibility, multiple hover previews are no longer supported.
|
||||
`,
|
||||
"1.6.34": `
|
||||
With 0.15.1 Obsidian is implementing some exciting, but significant changes to how windows are managed. I need to make some heavy/invasive changes to Excalidraw to adapt. The next version of the Excalidraw Plugin will require Obsidian 0.15.1 or newer. If you are not signed up for Obsidian Insider Builds, you will need to wait few weeks until the new Obsidian version will be made public.
|
||||
|
||||
# Fixed
|
||||
- Error saving when the attachments folder exists but with a different letter case (i.e. ATTACHMENTS instead of attachments) [658](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/658). I added more error tolerance. As a general rule, however, I recommend treating file paths as case-sensitive as some platforms like iOS or LINUX have case-sensitive filenames, and synchronizing your Vault to these platforms will cause you headaches in the future.
|
||||
- Text detached from the container if you immediately clicked the text-align buttons on the properties pane while still editing the text in the container for the very first time. [#657](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/657).
|
||||
- Can't add text to the second container if the first container has text and the second container is centered around the first one. [#5300](https://github.com/excalidraw/excalidraw/issues/5300)
|
||||
`,
|
||||
"1.6.33": `
|
||||
# Fixed
|
||||
- Under some special circumstances when you embedded a drawing (guest) into another drawing (host), the host did not update when you modified the guest, until you closed Excalidraw completely and reopened the host. [#637](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/637)
|
||||
|
||||
# New
|
||||
- ExcalidrawAutomate ${String.fromCharCode(96)}addLabelToLine${String.fromCharCode(96)} adds a text label to a line or arrow. Currently this function only works with simple straight 2-point (start & end) lines.
|
||||
${String.fromCharCode(96, 96, 96)}typescript
|
||||
addLabelToLine(lineId: string, label: string): string
|
||||
${String.fromCharCode(96, 96, 96)}
|
||||
- ExcalidrawAutomate ${String.fromCharCode(96)}ConnectObjects${String.fromCharCode(96)} now returns the ID of the arrow that was created.`,
|
||||
"1.6.32": `
|
||||
## Fixed
|
||||
- Filenames of embedded images and markdown documents did not get updated if the drawing was open in a work-pane while you changed the filename of the embedded file (image or markdown document) [632](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/632).
|
||||
- When you created a new text element and immediately dragged it, sometimes autosave interrupted the drag action and Excalidraw dropped the element you were dragging [630](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/630)
|
||||
- In some edge cases when you had the drawing open on your desktop and you also opened the same image on your tablet, Sync seemed to work in the background but the changes did not appear on the desktop until you closed and opened the drawing again. [629](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/629)
|
||||
- LaTeX support: Excalidraw must download a javascript library from one of the hosting sites for MathJax tex2svg. It seems that some people do not have access to the URL recommended in the first place by [MathJax](https://docs.mathjax.org/en/latest/web/start.html). If LaTeX formulas do not render correctly in Excalidraw, try changing the source server under Compatibility Settings in Excalidraw Plugin Settings. [628](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/628)`,
|
||||
"1.6.31": `
|
||||
Minor update:
|
||||
|
||||
## Fixes
|
||||
- Color picker hotkeys were not working. They are working again [627](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/627)
|
||||
- I updated MathJax (LaTeX) to the newest (3.2.1) release.`,
|
||||
"1.6.30": `
|
||||
## Fixed
|
||||
- The load stencil library button stopped working after 1.6.29 due to an error in the core Excalidraw package. It is now fixed. [#625](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/625).
|
||||
- On iPad (probably other Obsidian mobile devices as well) after opening the command palette the positioning of the pointer was off. From now on, the pointer is automatically re-calibrated every 5 seconds.
|
||||
- I improved shared-vault collaboration sync. If the open file has not been saved for the last 5 minutes (i.e. you are not working on the drawing actively), and a newer remote version of the file is received via sync, then the remote file will simply overwrite the local file (i.e. the behavior of Excalidraw Obsidian prior to implementing Shared (Multiplayer) Vault Synchronization support in 1.6.29). This solution will support active collaboration when parties participating are actively editing the drawing, but also caters to the scenario when you open a drawing on one device (e.g. your desktop) and once you are finished editing you do not close the drawing, but simply put your PC to sleep... then later you edit the same drawing on your tablet. When you turn your desktop PC on the next time, the changes you've made on your tablet will be synchronized by Obsidian sync. In this case the changes from your tablet should be honored. If you have not edited the open drawing for more then 5 minutes (like in this scenario) there is no value in running the file comparison between the local version and the received one. This approach reduces the probability of running into sync conflicts.`,
|
||||
"1.6.29": `
|
||||
## New
|
||||
- I implemented sync support inspired by the new [Obsidian Multiplayer Sync](https://youtu.be/ZyCPhbd51eo) feature (available in insider build v0.14.10).
|
||||
- To manage expectations, this is not real-time collaboration like on Excalidraw.com. Synchronization is delayed by the frequency of the autosave timer (every 10 secs) and the speed of Obsidian sync. Also if a file has conflicting versions, Obsidian sync may delay the delivery of the changed file.
|
||||
- Even if you are not using multiplayer Obsidian Vaults, you may benefit from the improved synchronization, for example when using the freedraw tool on your tablet or phone, and in parallel editing the same drawing (e.g. typing text) on your desktop. I frequently do this in a mind-mapping scenario.
|
||||
- If the same Excalidraw sketch is open on multiple devices then Excalidraw will try to merge changes into the open drawing, thus parallel modifications on different devices are possible. If the same element is edited by multiple parties at the same time, then the foreign (received) version will be honored and the local changes lost.
|
||||
|
||||
## Fixed:
|
||||
- Default embed width setting stopped working. [#622](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/622)
|
||||
- The link tooltip gets stuck on screen after Excalidraw closes [#621](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/621)
|
||||
- Layout error when using the Workspaces core plugin. [#28](https://github.com/zsviczian/excalibrain/issues/28)`,
|
||||
"1.6.28": `
|
||||
## New
|
||||
- When dropping a link from a DataView query into Excalidraw the link will honor your "New link format" preferences in Obsidian. It will add the "shortest path when possible", if that is your setting. If the link includes a block or section reference, then the link will automatically include an alias, such that only the filename is displayed (shortest path possible allowing) [#610](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/610)
|
||||
- If Excalidraw is in a Hover Editor and you open a link in another pane by CTRL+SHIFT+Click then the new page will open in the main workspace, and not in a split pane in the hover editor.
|
||||
|
||||
## Fixed
|
||||
- New text elements get de-selected after auto-save [#609](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/609)
|
||||
- Update opacity of bound text when the opacity of the container is updated [#5142](https://github.com/excalidraw/excalidraw/pull/5142)
|
||||
- ExcalidrawAutomate: openFileInNewOrAdjacentLeaf() function. This also caused an error when clicking a link in Excalidraw in a hover window, when there were no leaves in the main workspace view.`,
|
||||
"1.6.27": `
|
||||
## New Features
|
||||
- While these new features are benefitial for all Excalidraw Automation projects, the current changes are mainly in support of the [ExcaliBrain](https://youtu.be/O2s-h5VKCas) integration. See detailed [Release Notes](https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/1.6.27) on GitHub.
|
||||
`,
|
||||
"1.6.26": `
|
||||
## Fixed
|
||||
- Dragging multiple files onto the canvas will now correctly [#589](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/589)
|
||||
- add multiple links
|
||||
- or if you hold the CTRL/(SHIFT on Mac) while dropping the files, then adding multiple images
|
||||
- Dropped images and links were not selectable with the selection tool until the file was saved. This is now fixed.
|
||||
- Display the linked block/section on link-hover instead of the full page. [#597](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/597)
|
||||
- Hover preview without CTRL/CMD works again. Requires configuration in plugin settings. [#595](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/595)
|
||||
- If you embed the same markdown document into a drawing multiple times, you can now display different sections of the document in each embedded object. [#601](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/601).
|
||||
- If you make a copy of an equation and edit this copy, the original equation will remain unchanged [#593](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/593)
|
||||
|
||||
## New Features
|
||||
- When you drag files from Dataview-results onto the canvas the obsidian:// urls will be converted into wiki links.[#599](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/599)
|
||||
- I added one more frontmatter key: ${String.fromCharCode(96)}excalidraw-linkbutton-opacity: ${String.fromCharCode(96)} This sets the opacity of the blue link-button in the top right corner of the element, overriding the respective setting in plugin settings. Valid values are numbers between 0 and 1, where 0 means the button is fully transparent.
|
||||
|
||||
## New Excalidraw Automate Features
|
||||
- As part of building the new [ExcaliBrain](https://youtu.be/O2s-h5VKCas) plugin, I've added a number of integration features. See the GitHub [Release Notes](https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/1.6.26) for details.
|
||||
`,
|
||||
"1.6.25": `
|
||||
## Fixed
|
||||
- Pinch-zoom in view mode was broken ([#5001](https://github.com/excalidraw/excalidraw/pull/5001))
|
||||
- The add image button on iPad was not working ([#5038](https://github.com/excalidraw/excalidraw/pull/5038) & [#584](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/584))
|
||||
|
||||
## New Features
|
||||
- If Excalidraw is open in a [hover-editor](https://github.com/nothingislost/obsidian-hover-editor) when opening a link in a new pane Excalidraw will now open the link in the main workspace and not by splitting the view inside the hover-editor.
|
||||
- Excalidraw ScriptEngine settings
|
||||
- Script Engine settings now render HTML descriptions
|
||||
- If the ${String.fromCharCode(96)}height${String.fromCharCode(96)} property of a text setting is set, the corresponding text input field will be rendered as a textArea with the specified height.
|
||||
`,
|
||||
"1.6.24": `
|
||||
## Fixed
|
||||
|
||||
@@ -33,7 +33,7 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
|
||||
if (this.containerEl.innerText.includes(EMPTY_MESSAGE)) {
|
||||
this.plugin.createAndOpenDrawing(
|
||||
`${this.plugin.settings.folder}/${this.inputEl.value}.excalidraw.md`,
|
||||
this.onNewPane,
|
||||
this.onNewPane?"new-pane":"active-pane",
|
||||
);
|
||||
this.close();
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
|
||||
onChooseItem(item: TFile): void {
|
||||
switch (this.action) {
|
||||
case openDialogAction.openFile:
|
||||
this.plugin.openDrawing(item, this.onNewPane);
|
||||
this.plugin.openDrawing(item, this.onNewPane?"new-pane":"active-pane");
|
||||
break;
|
||||
case openDialogAction.insertLinkToDrawing:
|
||||
this.plugin.embedDrawing(item);
|
||||
|
||||
@@ -219,7 +219,7 @@ export class GenericInputPrompt extends Modal {
|
||||
}
|
||||
|
||||
private removeInputListener() {
|
||||
this.inputComponent.inputEl.removeEventListener(
|
||||
this.inputComponent?.inputEl?.removeEventListener(
|
||||
"keydown",
|
||||
this.submitEnterCallback,
|
||||
);
|
||||
@@ -370,6 +370,7 @@ export class NewFileActions extends Modal {
|
||||
private plugin: ExcalidrawPlugin,
|
||||
private path: string,
|
||||
private newPane: boolean,
|
||||
private newWindow: boolean,
|
||||
private view: ExcalidrawView,
|
||||
) {
|
||||
super(plugin.app);
|
||||
@@ -385,11 +386,14 @@ export class NewFileActions extends Modal {
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
const leaf = this.newPane
|
||||
? getNewOrAdjacentLeaf(this.plugin, this.view.leaf)
|
||||
: this.view.leaf;
|
||||
leaf.openFile(file);
|
||||
this.app.workspace.setActiveLeaf(leaf, true, true);
|
||||
const leaf = this.newWindow
|
||||
//@ts-ignore
|
||||
? app.workspace.openPopoutLeaf()
|
||||
: this.newPane
|
||||
? getNewOrAdjacentLeaf(this.plugin, this.view.leaf)
|
||||
: this.view.leaf;
|
||||
leaf.openFile(file, {active:true});
|
||||
//this.app.workspace.setActiveLeaf(leaf, true, true);
|
||||
}
|
||||
|
||||
createForm(): void {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { App, MarkdownRenderer, Modal } from "obsidian";
|
||||
import { isVersionNewerThanOther } from "src/utils/Utils";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { FIRST_RUN, RELEASE_NOTES } from "./Messages";
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
|
||||
export class ReleaseNotes extends Modal {
|
||||
private plugin: ExcalidrawPlugin;
|
||||
private version: string;
|
||||
@@ -9,7 +12,6 @@ export class ReleaseNotes extends Modal {
|
||||
constructor(app: App, plugin: ExcalidrawPlugin, version: string) {
|
||||
super(app);
|
||||
this.plugin = plugin;
|
||||
//@ts-ignore
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@@ -23,21 +25,19 @@ export class ReleaseNotes extends Modal {
|
||||
async onClose() {
|
||||
this.contentEl.empty();
|
||||
await this.plugin.loadSettings();
|
||||
this.plugin.settings.previousRelease =
|
||||
//@ts-ignore
|
||||
this.app.plugins.manifests["obsidian-excalidraw-plugin"].version;
|
||||
this.plugin.settings.previousRelease = PLUGIN_VERSION
|
||||
await this.plugin.saveSettings();
|
||||
}
|
||||
|
||||
async createForm() {
|
||||
let prevRelease = this.plugin.settings.previousRelease;
|
||||
prevRelease = this.version === prevRelease ? "0" : prevRelease;
|
||||
prevRelease = this.version === prevRelease ? "0.0.0" : prevRelease;
|
||||
const message = this.version
|
||||
? Object.keys(RELEASE_NOTES)
|
||||
.filter((key) => key > prevRelease)
|
||||
.filter((key) => key === "Intro" || isVersionNewerThanOther(key,prevRelease))
|
||||
.map((key: string) => `# ${key}\n${RELEASE_NOTES[key]}`)
|
||||
.slice(0, 6)
|
||||
.join("\n\n")
|
||||
.slice(0, 10)
|
||||
.join("\n\n---\n")
|
||||
: FIRST_RUN;
|
||||
await MarkdownRenderer.renderMarkdown(
|
||||
message,
|
||||
|
||||
@@ -236,10 +236,16 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
},
|
||||
{
|
||||
field: "connectObjects",
|
||||
code: "connectObjects(objectA: string, connectionA: ConnectionPoint, objectB: string, connectionB: ConnectionPoint, formatting?: {numberOfPoints?: number; startArrowHead?: string; endArrowHead?: string; padding?: number;},): void;",
|
||||
code: "connectObjects(objectA: string, connectionA: ConnectionPoint, objectB: string, connectionB: ConnectionPoint, formatting?: {numberOfPoints?: number; startArrowHead?: string; endArrowHead?: string; padding?: number;},): string;",
|
||||
desc: 'type ConnectionPoint = "top" | "bottom" | "left" | "right" | null\nWhen null is passed as ConnectionPoint then Excalidraw will automatically decide\nnumberOfPoints is the number of points on the line. Default is 0 i.e. line will only have a start and end point.\nArrowHead: "triangle"|"dot"|"arrow"|"bar"|null',
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "addLabelToLine",
|
||||
code: "addLabelToLine(lineId: string, label: string): string;",
|
||||
desc: 'Adds a text label to a line or arrow. Currently only works with a simple straight 2-point (start & end) line',
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "clear",
|
||||
code: "clear(): void;",
|
||||
@@ -525,6 +531,19 @@ export const FRONTMATTER_KEYS_INFO: SuggesterInfo[] = [
|
||||
desc: "Specifies how Excalidraw should open by default. Valid values are: view|zen",
|
||||
after: ": view",
|
||||
},
|
||||
{
|
||||
field: "linkbutton-opacity",
|
||||
code: null,
|
||||
desc: "The opacity of the blue link button in the top right of the element overriding the respective setting in plugin settings. "+
|
||||
"Valid values are between 0 and 1, where 0 means the button is transparent.",
|
||||
after: ": 0.5",
|
||||
},
|
||||
{
|
||||
field: "onload-script",
|
||||
code: null,
|
||||
desc: "The value of this field will be executed as javascript code using the Script Engine environment. Use this to initiate custom actions or logic when loading your drawing.",
|
||||
after: ': "new Notice(`Hello World!\\n\\nFile: ${ea.targetView.file.basename}`);"',
|
||||
},
|
||||
{
|
||||
field: "font",
|
||||
code: null,
|
||||
@@ -573,4 +592,11 @@ export const FRONTMATTER_KEYS_INFO: SuggesterInfo[] = [
|
||||
desc: "If this key is present it will override the default excalidraw embed and export setting. This only affects export to PNG. Specify the export scale for the image. The typical range is between 0.5 and 5, but you can experiment with other values as well.",
|
||||
after: ": 1",
|
||||
},
|
||||
{
|
||||
field: "autoexport",
|
||||
code: null,
|
||||
desc: "Override autoexport settings for this file. Valid values are\nnone\nboth\npng\nsvg",
|
||||
after: ": png",
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
13
src/index.ts
Normal file
13
src/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import "obsidian";
|
||||
//import { ExcalidrawAutomate } from "./ExcalidrawAutomate";
|
||||
export {ExcalidrawAutomateInterface} from "./types";
|
||||
export type { ExcalidrawBindableElement, ExcalidrawElement, FileId, FillStyle, StrokeSharpness, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
|
||||
export type { Point } from "@zsviczian/excalidraw/types/types";
|
||||
export const getEA = (view?:any): any => {
|
||||
try {
|
||||
return window.ExcalidrawAutomate.getAPI(view);
|
||||
} catch(e) {
|
||||
console.log({message: "Excalidraw not available", fn: getEA});
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,12 @@ export default {
|
||||
TRANSCLUDE_MOST_RECENT: "Transclude (embed) the most recently edited drawing",
|
||||
NEW_IN_NEW_PANE: "Create a new drawing - IN A NEW PANE",
|
||||
NEW_IN_ACTIVE_PANE: "Create a new drawing - IN THE CURRENT ACTIVE PANE",
|
||||
NEW_IN_POPOUT_WINDOW: "Create a new drawing - IN A POPOUT WINDOW",
|
||||
NEW_IN_NEW_PANE_EMBED:
|
||||
"Create a new drawing - IN A NEW PANE - and embed into active document",
|
||||
NEW_IN_ACTIVE_PANE_EMBED:
|
||||
"Create a new drawing - IN THE CURRENT ACTIVE PANE - and embed into active document",
|
||||
NEW_IN_POPOUT_WINDOW_EMBED: "Create a new drawing - IN A POPOUT WINDOW - and embedd into active document",
|
||||
EXPORT_SVG: "Save as SVG next to the current file",
|
||||
EXPORT_PNG: "Save as PNG next to the current file",
|
||||
TOGGLE_LOCK: "Toggle Text Element edit RAW/PREVIEW",
|
||||
@@ -189,10 +191,14 @@ export default {
|
||||
"If you don't want text accidentally changing in your drawings use <code>[[links|with aliases]]</code>.",
|
||||
ADJACENT_PANE_NAME: "Open in adjacent pane",
|
||||
ADJACENT_PANE_DESC:
|
||||
"When CTRL/CMD+SHIFT clicking a link in Excalidraw by default the plugin will open the link in a new pane. " +
|
||||
"When CTRL/CMD+SHIFT clicking a link in Excalidraw, by default the plugin will open the link in a new pane. " +
|
||||
"Turning this setting on, Excalidraw will first look for an existing adjacent pane, and try to open the link there. " +
|
||||
"Excalidraw will first look too the right, then to the left, then down, then up. If no pane is found, Excalidraw will open " +
|
||||
"a new pane.",
|
||||
"Excalidraw will look for the adjacent pane based on your focus/navigation history, i.e. the workpane that was active before you " +
|
||||
"activated Excalidraw.",
|
||||
MAINWORKSPACE_PANE_NAME: "Open in main workspace",
|
||||
MAINWORKSPACE_PANE_DESC:
|
||||
"When CTRL/CMD+SHIFT clicking a link in Excalidraw, by default the plugin will open the link in a new pane in the current active window. " +
|
||||
"Turning this setting on, Excalidraw will open the link in an existing or new pane in the main workspace. ",
|
||||
LINK_BRACKETS_NAME: "Show <code>[[brackets]]</code> around links",
|
||||
LINK_BRACKETS_DESC: `${
|
||||
"In PREVIEW mode, when parsing Text Elements, place brackets around links. " +
|
||||
@@ -210,7 +216,9 @@ export default {
|
||||
}${FRONTMATTER_KEY_CUSTOM_URL_PREFIX}: "🌐 "</code> to the file's frontmatter.`,
|
||||
HOVERPREVIEW_NAME: "Hover preview without CTRL/CMD key",
|
||||
HOVERPREVIEW_DESC:
|
||||
"Toggle On: Hover preview for [[wiki links]] is shown immediately, without the need to hold the CTRL/CMD key.<br>Toggle Off: Hover preview is shown only when you hold the CTRL/CMD key while hovering the link.",
|
||||
"<b>Toggle On</b>: In Exalidraw <u>view mode</u> the hover preview for [[wiki links]] will be shown immediately, without the need to hold the CTRL/CMD key. " +
|
||||
"In Excalidraw <u>normal mode</u>, the preview will be shown immediately only when hovering the blue link icon in the top right of the element.<br> " +
|
||||
"<b>Toggle Off</b>: Hover preview is shown only when you hold the CTRL/CMD key while hovering the link.",
|
||||
LINKOPACITY_NAME: "Opacity of link icon",
|
||||
LINKOPACITY_DESC:
|
||||
"Opacity of the link indicator icon in the top right corner of an element. 1 is opaque, 0 is transparent.",
|
||||
@@ -328,7 +336,9 @@ export default {
|
||||
"Automatically create an SVG export of your drawing matching the title of your file. " +
|
||||
"The plugin will save the *.SVG file in the same folder as the drawing. " +
|
||||
"Embed the .svg file into your documents instead of Excalidraw making you embeds platform independent. " +
|
||||
"While the auto-export switch is on, this file will get updated every time you edit the Excalidraw drawing with the matching name.",
|
||||
"While the auto-export switch is on, this file will get updated every time you edit the Excalidraw drawing with the matching name. " +
|
||||
"You can override this setting on a file level by adding the <code>excalidraw-autoexport</code> frontmatter key. Valid values for this key are " +
|
||||
"<code>none</code>,<code>both</code>,<code>svg</code>, and <code>png</code>",
|
||||
EXPORT_PNG_NAME: "Auto-export PNG",
|
||||
EXPORT_PNG_DESC: "Same as the auto-export SVG, but for *.PNG",
|
||||
COMPATIBILITY_HEAD: "Compatibility features",
|
||||
@@ -344,6 +354,10 @@ export default {
|
||||
"By enabling this feature drawings you create with the ribbon icon, the command palette actions, " +
|
||||
"and the file explorer are going to be all legacy *.excalidraw files. This setting will also turn off the reminder message " +
|
||||
"when you open a legacy file for editing.",
|
||||
MATHJAX_NAME: "MathJax (LaTeX) javascript library host",
|
||||
MATHJAX_DESC: "If you are using LaTeX equiations in Excalidraw then the plugin needs to load a javascript library for that. " +
|
||||
"Some users are unable to access certain host servers. If you are experiencing issues try changing the host here. You may need to "+
|
||||
"restart Obsidian after closing settings, for this change to take effect.",
|
||||
EXPERIMENTAL_HEAD: "Experimental features",
|
||||
EXPERIMENTAL_DESC:
|
||||
"Some of these setting will not take effect immediately, only when the File Explorer is refreshed, or Obsidian restarted.",
|
||||
|
||||
526
src/main.ts
526
src/main.ts
@@ -15,7 +15,7 @@ import {
|
||||
loadMathJax,
|
||||
request,
|
||||
MetadataCache,
|
||||
FrontMatterCache,
|
||||
FrontMatterCache
|
||||
} from "obsidian";
|
||||
import {
|
||||
BLANK_DRAWING,
|
||||
@@ -42,10 +42,11 @@ import {
|
||||
VIRGIL_FONT,
|
||||
VIRGIL_DATAURL,
|
||||
} from "./Constants";
|
||||
import ExcalidrawView, { TextMode } from "./ExcalidrawView";
|
||||
import ExcalidrawView, { TextMode, getTextMode } from "./ExcalidrawView";
|
||||
import {
|
||||
changeThemeOfExcalidrawMD,
|
||||
getMarkdownDrawingSection,
|
||||
ExcalidrawData
|
||||
} from "./ExcalidrawData";
|
||||
import {
|
||||
ExcalidrawSettings,
|
||||
@@ -81,8 +82,10 @@ import {
|
||||
log,
|
||||
setLeftHandedMode,
|
||||
sleep,
|
||||
debug,
|
||||
isVersionNewerThanOther,
|
||||
} from "./utils/Utils";
|
||||
import { getAttachmentsFolderAndFilePath, isObsidianThemeDark } from "./utils/ObsidianUtils";
|
||||
import { getAttachmentsFolderAndFilePath, getNewOrAdjacentLeaf, getParentOfClass, isObsidianThemeDark } from "./utils/ObsidianUtils";
|
||||
//import { OneOffs } from "./OneOffs";
|
||||
import { FileId } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { ScriptEngine } from "./Scripts";
|
||||
@@ -94,7 +97,10 @@ import {
|
||||
} from "./MarkdownPostProcessor";
|
||||
import { FieldSuggester } from "./dialogs/FieldSuggester";
|
||||
import { ReleaseNotes } from "./dialogs/ReleaseNotes";
|
||||
import { debug } from "./utils/Utils";
|
||||
import { decompressFromBase64 } from "lz-string";
|
||||
import { Packages } from "./types";
|
||||
import * as React from "react";
|
||||
|
||||
|
||||
declare module "obsidian" {
|
||||
interface App {
|
||||
@@ -115,6 +121,12 @@ declare module "obsidian" {
|
||||
}
|
||||
}
|
||||
|
||||
declare const EXCALIDRAW_PACKAGES:string;
|
||||
declare const react:any;
|
||||
declare const reactDOM:any;
|
||||
declare const excalidrawLib: any;
|
||||
declare const PLUGIN_VERSION:string;
|
||||
|
||||
export default class ExcalidrawPlugin extends Plugin {
|
||||
private excalidrawFiles: Set<TFile> = new Set<TFile>();
|
||||
public excalidrawFileModes: { [file: string]: string } = {};
|
||||
@@ -139,7 +151,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
public opencount: number = 0;
|
||||
public ea: ExcalidrawAutomate;
|
||||
//A master list of fileIds to facilitate copy / paste
|
||||
public filesMaster: Map<FileId, { path: string; hasSVGwithBitmap: boolean }> =
|
||||
public filesMaster: Map<FileId, { path: string; hasSVGwithBitmap: boolean; blockrefData: string }> =
|
||||
null; //fileId, path
|
||||
public equationsMaster: Map<FileId, string> = null; //fileId, formula
|
||||
public mathjax: any = null;
|
||||
@@ -147,15 +159,38 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
public mathjaxLoaderFinished: boolean = false;
|
||||
public scriptEngine: ScriptEngine;
|
||||
public fourthFontDef: string = VIRGIL_FONT;
|
||||
private packageMap: WeakMap<Window,Packages> = new WeakMap<Window,Packages>();
|
||||
public leafChangeTimeout: NodeJS.Timeout = null;
|
||||
|
||||
constructor(app: App, manifest: PluginManifest) {
|
||||
super(app, manifest);
|
||||
this.filesMaster = new Map<
|
||||
FileId,
|
||||
{ path: string; hasSVGwithBitmap: boolean }
|
||||
{ path: string; hasSVGwithBitmap: boolean; blockrefData: string }
|
||||
>();
|
||||
this.equationsMaster = new Map<FileId, string>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public getPackage(win:Window):Packages {
|
||||
if(win===window) {
|
||||
return {react, reactDOM, excalidrawLib};
|
||||
}
|
||||
if(this.packageMap.has(win)) {
|
||||
return this.packageMap.get(win);
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
const {react:r, reactDOM:rd, excalidrawLib:e} = win.eval.call(win,
|
||||
`(function() {
|
||||
${decompressFromBase64(EXCALIDRAW_PACKAGES)};
|
||||
return {react:React,reactDOM:ReactDOM,excalidrawLib:ExcalidrawLib};
|
||||
})()`);
|
||||
this.packageMap.set(win,{react:r, reactDOM:rd, excalidrawLib:e});
|
||||
return {react:r, reactDOM:rd, excalidrawLib:e};
|
||||
}
|
||||
|
||||
async onload() {
|
||||
addIcon(ICON_NAME, EXCALIDRAW_ICON);
|
||||
addIcon(SCRIPTENGINE_ICON_NAME, SCRIPTENGINE_ICON);
|
||||
@@ -188,29 +223,16 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
//https://github.com/mgmeyers/obsidian-kanban/blob/44118e25661bff9ebfe54f71ae33805dc88ffa53/src/main.ts#L267
|
||||
this.registerMonkeyPatches();
|
||||
|
||||
if (!this.app.isMobile) {
|
||||
const electron: string = process?.versions?.electron;
|
||||
if (electron && electron?.startsWith("8.")) {
|
||||
new Notice(
|
||||
`You are running an older version of the electron Browser (${electron}). If Excalidraw does not start up, please reinstall Obsidian with the latest installer and try again.`,
|
||||
10000,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// const patches = new OneOffs(this);
|
||||
if (this.settings.showReleaseNotes) {
|
||||
//I am repurposing imageElementNotice, if the value is true, this means the plugin was just newly installed to Obsidian.
|
||||
const obsidianJustInstalled = this.settings.imageElementNotice;
|
||||
|
||||
const version: string =
|
||||
//@ts-ignore
|
||||
this.app.plugins.manifests["obsidian-excalidraw-plugin"].version;
|
||||
if (version > this.settings.previousRelease) {
|
||||
|
||||
if (isVersionNewerThanOther(PLUGIN_VERSION, this.settings.previousRelease)) {
|
||||
new ReleaseNotes(
|
||||
this.app,
|
||||
this,
|
||||
obsidianJustInstalled ? null : version,
|
||||
obsidianJustInstalled ? null : PLUGIN_VERSION,
|
||||
).open();
|
||||
}
|
||||
}
|
||||
@@ -236,32 +258,44 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
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;
|
||||
|
||||
const visitedDocs = new Set<Document>();
|
||||
app.workspace.iterateAllLeaves((leaf)=>{
|
||||
const ownerDocument = app.isMobile?document:leaf.view.containerEl.ownerDocument;
|
||||
if(!ownerDocument) return;
|
||||
if(visitedDocs.has(ownerDocument)) return;
|
||||
visitedDocs.add(ownerDocument);
|
||||
// replace the old local font <style> element with the one we just created
|
||||
const newStylesheet = ownerDocument.createElement("style");
|
||||
newStylesheet.id = "local-font-stylesheet";
|
||||
newStylesheet.textContent = `
|
||||
@font-face {
|
||||
font-family: 'LocalFont';
|
||||
src: url("${fourthFontDataURL}");
|
||||
font-display: swap;
|
||||
}
|
||||
`;
|
||||
const oldStylesheet = ownerDocument.getElementById(newStylesheet.id);
|
||||
ownerDocument.head.appendChild(newStylesheet);
|
||||
if (oldStylesheet) {
|
||||
ownerDocument.head.removeChild(oldStylesheet);
|
||||
}
|
||||
`;
|
||||
// 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`);
|
||||
ownerDocument.fonts.load('20px LocalFont');
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
private loadMathJax() {
|
||||
public loadMathJax() {
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
//loading Obsidian MathJax as fallback
|
||||
await loadMathJax();
|
||||
try {
|
||||
if(self.mathjaxDiv) {
|
||||
document.body.removeChild(self.mathjaxDiv);
|
||||
self.mathjax = null;
|
||||
self.mathjaxLoaderFinished = false;
|
||||
}
|
||||
self.mathjaxDiv = document.body.createDiv();
|
||||
self.mathjaxDiv.title = "Excalidraw MathJax Support";
|
||||
self.mathjaxDiv.style.display = "none";
|
||||
@@ -299,7 +333,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
self.mathjaxLoaderFinished = true;
|
||||
});
|
||||
};
|
||||
script.src = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js";
|
||||
script.src = self.settings.mathjaxSourceURL; // "https://cdn.jsdelivr.net/npm/mathjax@3.2.1/es5/tex-svg.js";
|
||||
//script.src = MATHJAX_DATAURL;
|
||||
doc.head.appendChild(script);
|
||||
} catch {
|
||||
@@ -633,7 +667,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
this.addRibbonIcon(ICON_NAME, t("CREATE_NEW"), async (e) => {
|
||||
this.createAndOpenDrawing(
|
||||
getDrawingFilename(this.settings),
|
||||
e[CTRL_OR_CMD],
|
||||
e[CTRL_OR_CMD]?"new-pane":"active-pane",
|
||||
); //.ctrlKey||e.metaKey);
|
||||
});
|
||||
|
||||
@@ -651,7 +685,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
this.createAndOpenDrawing(
|
||||
getDrawingFilename(this.settings),
|
||||
false,
|
||||
"active-pane",
|
||||
folderpath,
|
||||
);
|
||||
});
|
||||
@@ -723,7 +757,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("TRANSCLUDE"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return this.app.workspace.activeLeaf.view.getViewType() == "markdown";
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(MarkdownView))
|
||||
}
|
||||
this.openDialog.start(openDialogAction.insertLinkToDrawing, false);
|
||||
return true;
|
||||
@@ -736,7 +770,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return (
|
||||
this.app.workspace.activeLeaf.view.getViewType() == "markdown" &&
|
||||
Boolean(this.app.workspace.getActiveViewOfType(MarkdownView)) &&
|
||||
this.lastActiveExcalidrawFilePath != null
|
||||
);
|
||||
}
|
||||
@@ -755,7 +789,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
id: "excalidraw-autocreate",
|
||||
name: t("NEW_IN_NEW_PANE"),
|
||||
callback: () => {
|
||||
this.createAndOpenDrawing(getDrawingFilename(this.settings), true);
|
||||
this.createAndOpenDrawing(getDrawingFilename(this.settings), "new-pane");
|
||||
},
|
||||
});
|
||||
|
||||
@@ -763,11 +797,24 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
id: "excalidraw-autocreate-on-current",
|
||||
name: t("NEW_IN_ACTIVE_PANE"),
|
||||
callback: () => {
|
||||
this.createAndOpenDrawing(getDrawingFilename(this.settings), false);
|
||||
this.createAndOpenDrawing(getDrawingFilename(this.settings), "active-pane");
|
||||
},
|
||||
});
|
||||
|
||||
const insertDrawingToDoc = async (inNewPane: boolean) => {
|
||||
this.addCommand({
|
||||
id: "excalidraw-autocreate-popout",
|
||||
name: t("NEW_IN_POPOUT_WINDOW"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return !app.isMobile
|
||||
}
|
||||
this.createAndOpenDrawing(getDrawingFilename(this.settings), "popout-window");
|
||||
},
|
||||
});
|
||||
|
||||
const insertDrawingToDoc = async (
|
||||
location: "active-pane"|"new-pane"|"popout-window"
|
||||
) => {
|
||||
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
if (!activeView) {
|
||||
return;
|
||||
@@ -787,7 +834,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
).folder;
|
||||
const file = await this.createDrawing(filename, folder);
|
||||
await this.embedDrawing(file);
|
||||
this.openDrawing(file, inNewPane);
|
||||
this.openDrawing(file, location);
|
||||
};
|
||||
|
||||
this.addCommand({
|
||||
@@ -795,9 +842,9 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("NEW_IN_NEW_PANE_EMBED"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return this.app.workspace.activeLeaf.view.getViewType() == "markdown";
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(MarkdownView));
|
||||
}
|
||||
insertDrawingToDoc(true);
|
||||
insertDrawingToDoc("new-pane");
|
||||
return true;
|
||||
},
|
||||
});
|
||||
@@ -807,25 +854,36 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("NEW_IN_ACTIVE_PANE_EMBED"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return this.app.workspace.activeLeaf.view.getViewType() == "markdown";
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(MarkdownView));
|
||||
}
|
||||
insertDrawingToDoc(false);
|
||||
insertDrawingToDoc("active-pane");
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "excalidraw-autocreate-and-embed-popout",
|
||||
name: t("NEW_IN_POPOUT_WINDOW_EMBED"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return !app.isMobile && Boolean(this.app.workspace.getActiveViewOfType(MarkdownView));
|
||||
}
|
||||
insertDrawingToDoc("popout-window");
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "export-svg",
|
||||
name: t("EXPORT_SVG"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return (
|
||||
this.app.workspace.activeLeaf.view.getViewType() ==
|
||||
VIEW_TYPE_EXCALIDRAW
|
||||
Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
);
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
view.saveSVG();
|
||||
return true;
|
||||
}
|
||||
@@ -839,12 +897,11 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return (
|
||||
this.app.workspace.activeLeaf.view.getViewType() ===
|
||||
VIEW_TYPE_EXCALIDRAW
|
||||
Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
);
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
search(view);
|
||||
return true;
|
||||
}
|
||||
@@ -858,12 +915,11 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return (
|
||||
this.app.workspace.activeLeaf.view.getViewType() ===
|
||||
VIEW_TYPE_EXCALIDRAW
|
||||
Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
);
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
if (view.isFullscreen()) {
|
||||
view.exitFullscreen();
|
||||
} else {
|
||||
@@ -875,67 +931,17 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
},
|
||||
});
|
||||
|
||||
/* this.addCommand({
|
||||
id: "ocr",
|
||||
name: "Test OCR",//t("EXPORT_PNG"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return (
|
||||
this.app.workspace.activeLeaf.view.getViewType() ===
|
||||
//@ts-ignore
|
||||
VIEW_TYPE_EXCALIDRAW && typeof Tesseract !== "undefined"
|
||||
);
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
//@ts-ignore
|
||||
const worker = Tesseract.createWorker({logger: m => console.log(m)});
|
||||
//@ts-ignore
|
||||
Tesseract.setLogging(true);
|
||||
|
||||
const exportSettings: ExportSettings = {
|
||||
withBackground: true,
|
||||
withTheme: false,
|
||||
};
|
||||
const blobToBase64 = async (blob:any) => {
|
||||
return new Promise((resolve, _) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result);
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const png = await getPNG(
|
||||
view.getScene(),
|
||||
exportSettings,
|
||||
3,
|
||||
);
|
||||
await worker.load();
|
||||
await worker.loadLanguage('https://https://raw.githubusercontent.com/thecodingone/trained-tesseract-handwriting-fonts/blob/master/eng.traineddata');
|
||||
await worker.initialize('https://raw.githubusercontent.com/thecodingone/trained-tesseract-handwriting-fonts/blob/master/eng.traineddata');
|
||||
const { data: { text } } = await worker.recognize(await blobToBase64(png));
|
||||
console.log(text);
|
||||
await worker.terminate();
|
||||
})();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});*/
|
||||
|
||||
this.addCommand({
|
||||
id: "export-png",
|
||||
name: t("EXPORT_PNG"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return (
|
||||
this.app.workspace.activeLeaf.view.getViewType() ==
|
||||
VIEW_TYPE_EXCALIDRAW
|
||||
Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
);
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
view.savePNG();
|
||||
return true;
|
||||
}
|
||||
@@ -950,16 +956,15 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
if (
|
||||
this.app.workspace.activeLeaf.view.getViewType() ===
|
||||
VIEW_TYPE_EXCALIDRAW
|
||||
Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
) {
|
||||
return !(this.app.workspace.activeLeaf.view as ExcalidrawView)
|
||||
return !(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
.compatibilityMode;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
view.changeTextMode(
|
||||
view.textMode === TextMode.parsed ? TextMode.raw : TextMode.parsed,
|
||||
);
|
||||
@@ -974,11 +979,10 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("DELETE_FILE"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
return view instanceof ExcalidrawView;
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
this.ea.reset();
|
||||
this.ea.setView(view);
|
||||
const el = this.ea.getViewSelectedElement();
|
||||
@@ -1011,11 +1015,10 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("INSERT_LINK"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
return view instanceof ExcalidrawView;
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
this.insertLinkDialog.start(view.file.path, view.addText);
|
||||
return true;
|
||||
}
|
||||
@@ -1029,11 +1032,10 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("INSERT_LINK_TO_ELEMENT"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
return view instanceof ExcalidrawView;
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
view.copyLinkToSelectedElementToClipboard();
|
||||
return true;
|
||||
}
|
||||
@@ -1046,11 +1048,10 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("INSERT_IMAGE"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
return view instanceof ExcalidrawView;
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
this.insertImageDialog.start(view);
|
||||
return true;
|
||||
}
|
||||
@@ -1063,13 +1064,9 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("READ_RELEASE_NOTES"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
return view instanceof ExcalidrawView;
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
}
|
||||
const version: string =
|
||||
//@ts-ignore
|
||||
this.app.plugins.manifests["obsidian-excalidraw-plugin"].version;
|
||||
new ReleaseNotes(this.app, this, version).open();
|
||||
new ReleaseNotes(this.app, this, PLUGIN_VERSION).open();
|
||||
return true;
|
||||
},
|
||||
});
|
||||
@@ -1079,8 +1076,8 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("TRAY_MODE"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (!(view instanceof ExcalidrawView) || !view.excalidrawRef) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (!view || !view.excalidrawRef) {
|
||||
return false;
|
||||
}
|
||||
const st = view.excalidrawAPI.getAppState();
|
||||
@@ -1089,8 +1086,8 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView && view.excalidrawAPI) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view && view.excalidrawAPI) {
|
||||
view.toggleTrayMode();
|
||||
return true;
|
||||
}
|
||||
@@ -1103,11 +1100,10 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("INSERT_MD"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
return view instanceof ExcalidrawView;
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
this.insertMDDialog.start(view);
|
||||
return true;
|
||||
}
|
||||
@@ -1120,13 +1116,10 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("INSERT_LATEX"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return (
|
||||
this.app.workspace.activeLeaf.view.getViewType() ==
|
||||
VIEW_TYPE_EXCALIDRAW
|
||||
);
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView));
|
||||
}
|
||||
const view = this.app.workspace.activeLeaf.view;
|
||||
if (view instanceof ExcalidrawView) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
insertLaTeXToView(view);
|
||||
return true;
|
||||
}
|
||||
@@ -1146,25 +1139,30 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
if (checking) {
|
||||
if (
|
||||
this.app.workspace.activeLeaf.view.getViewType() ==
|
||||
VIEW_TYPE_EXCALIDRAW
|
||||
Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
) {
|
||||
return !(this.app.workspace.activeLeaf.view as ExcalidrawView)
|
||||
return !(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
.compatibilityMode;
|
||||
}
|
||||
return fileIsExcalidraw;
|
||||
}
|
||||
|
||||
const activeLeaf = this.app.workspace.activeLeaf;
|
||||
|
||||
if (activeLeaf?.view && activeLeaf.view instanceof ExcalidrawView) {
|
||||
const excalidrawView = this.app.workspace.getActiveViewOfType(ExcalidrawView)
|
||||
if (excalidrawView) {
|
||||
const activeLeaf = excalidrawView.leaf;
|
||||
this.excalidrawFileModes[(activeLeaf as any).id || activeFile.path] =
|
||||
"markdown";
|
||||
this.setMarkdownView(activeLeaf);
|
||||
} else if (fileIsExcalidraw) {
|
||||
return;
|
||||
}
|
||||
|
||||
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView)
|
||||
if (markdownView && fileIsExcalidraw) {
|
||||
const activeLeaf = markdownView.leaf;
|
||||
this.excalidrawFileModes[(activeLeaf as any).id || activeFile.path] =
|
||||
VIEW_TYPE_EXCALIDRAW;
|
||||
this.setExcalidrawView(activeLeaf);
|
||||
return;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1174,9 +1172,9 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
name: t("CONVERT_NOTE_TO_EXCALIDRAW"),
|
||||
checkCallback: (checking) => {
|
||||
const activeFile = this.app.workspace.getActiveFile();
|
||||
const activeLeaf = this.app.workspace.activeLeaf;
|
||||
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
|
||||
if (!activeFile || !activeLeaf) {
|
||||
if (!activeFile || !activeView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1185,13 +1183,14 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
if (checking) {
|
||||
return isFileEmpty;
|
||||
}
|
||||
|
||||
if (isFileEmpty) {
|
||||
(async () => {
|
||||
await this.app.vault.modify(
|
||||
activeFile,
|
||||
await this.getBlankDrawing(),
|
||||
);
|
||||
this.setExcalidrawView(activeLeaf);
|
||||
this.setExcalidrawView(activeView.leaf);
|
||||
})();
|
||||
}
|
||||
},
|
||||
@@ -1265,8 +1264,52 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
|
||||
private registerMonkeyPatches() {
|
||||
const self = this;
|
||||
this.registerEvent(
|
||||
app.workspace.on("editor-menu", (menu, editor, view) => {
|
||||
if(!view || !(view instanceof MarkdownView)) return;
|
||||
const file = view.file;
|
||||
const leaf = view.leaf;
|
||||
if (!view.file) return;
|
||||
const cache = this.app.metadataCache.getFileCache(file);
|
||||
if (!cache?.frontmatter || !cache.frontmatter[FRONTMATTER_KEY]) return;
|
||||
|
||||
menu.addItem(item => item
|
||||
.setTitle(t("OPEN_AS_EXCALIDRAW"))
|
||||
.setIcon(ICON_NAME)
|
||||
.setSection("excalidraw")
|
||||
.onClick(() => {
|
||||
//@ts-ignore
|
||||
this.excalidrawFileModes[leaf.id || file.path] = VIEW_TYPE_EXCALIDRAW;
|
||||
this.setExcalidrawView(leaf);
|
||||
}));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.registerEvent(
|
||||
app.workspace.on("file-menu", (menu, file, source, leaf) => {
|
||||
if (!leaf || !(leaf.view instanceof MarkdownView)) return;
|
||||
if (!(file instanceof TFile)) return;
|
||||
const cache = this.app.metadataCache.getFileCache(file);
|
||||
if (!cache?.frontmatter || !cache.frontmatter[FRONTMATTER_KEY]) return;
|
||||
|
||||
menu.addItem(item => {
|
||||
item
|
||||
.setTitle(t("OPEN_AS_EXCALIDRAW"))
|
||||
.setIcon(ICON_NAME)
|
||||
.setSection("pane")
|
||||
.onClick(() => {
|
||||
//@ts-ignore
|
||||
this.excalidrawFileModes[leaf.id || file.path] = VIEW_TYPE_EXCALIDRAW;
|
||||
this.setExcalidrawView(leaf);
|
||||
})});
|
||||
//@ts-ignore
|
||||
menu.items.unshift(menu.items.pop());
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const self = this;
|
||||
// Monkey patch WorkspaceLeaf to open Excalidraw drawings with ExcalidrawView by default
|
||||
this.register(
|
||||
around(WorkspaceLeaf.prototype, {
|
||||
@@ -1323,9 +1366,9 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
);
|
||||
|
||||
// Add a menu item to go back to Excalidraw view
|
||||
this.register(
|
||||
/*this.register(
|
||||
around(MarkdownView.prototype, {
|
||||
onMoreOptionsMenu(next) {
|
||||
onPaneMenu(next) {
|
||||
return function (menu: Menu) {
|
||||
const file = this.file;
|
||||
const cache = file
|
||||
@@ -1345,6 +1388,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
item
|
||||
.setTitle(t("OPEN_AS_EXCALIDRAW"))
|
||||
.setIcon(ICON_NAME)
|
||||
.setSection("pane")
|
||||
.onClick(() => {
|
||||
self.excalidrawFileModes[this.leaf.id || file.path] =
|
||||
VIEW_TYPE_EXCALIDRAW;
|
||||
@@ -1352,38 +1396,17 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
});
|
||||
})
|
||||
.addSeparator();
|
||||
|
||||
next.call(this, menu);
|
||||
};
|
||||
},
|
||||
}),
|
||||
);
|
||||
);*/
|
||||
}
|
||||
|
||||
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 (
|
||||
@@ -1414,7 +1437,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
const modifyEventHandler = async (file: TFile) => {
|
||||
const leaves = self.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
leaves.forEach((leaf: WorkspaceLeaf) => {
|
||||
leaves.forEach(async (leaf: WorkspaceLeaf) => {
|
||||
const excalidrawView = leaf.view as ExcalidrawView;
|
||||
if (
|
||||
excalidrawView.file &&
|
||||
@@ -1425,8 +1448,24 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
file.path.lastIndexOf(".excalidraw"),
|
||||
)}.md` === excalidrawView.file.path))
|
||||
) {
|
||||
//debug({where:"ExcalidrawPlugin.modifyEventHandler",file:file.name,reloadfile:excalidrawView.file,before:"reload(true)"});
|
||||
excalidrawView.reload(true, excalidrawView.file);
|
||||
if(excalidrawView.semaphores.preventReload) {
|
||||
excalidrawView.semaphores.preventReload = false;
|
||||
return;
|
||||
}
|
||||
//if the user hasn't touched the file for 5 minutes, don't synchronize, reload.
|
||||
//this is to avoid complex sync scenarios of multiple remote changes outside an active collaboration session
|
||||
if(excalidrawView.lastSaveTimestamp + 300000 < Date.now()) {
|
||||
excalidrawView.reload(true, excalidrawView.file);
|
||||
return;
|
||||
}
|
||||
if(file.extension==="md") {
|
||||
const inData = new ExcalidrawData(self);
|
||||
const data = await app.vault.read(file);
|
||||
await inData.loadData(data,file,getTextMode(data));
|
||||
excalidrawView.synchronizeWithData(inData);
|
||||
} else {
|
||||
excalidrawView.reload(true, excalidrawView.file);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1484,6 +1523,12 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
//save Excalidraw leaf and update embeds when switching to another leaf
|
||||
const activeLeafChangeEventHandler = async (leaf: WorkspaceLeaf) => {
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/723
|
||||
if(self.leafChangeTimeout) {
|
||||
clearTimeout(this.leafChangeTimeout);
|
||||
}
|
||||
self.leafChangeTimeout = setTimeout(()=>{self.leafChangeTimeout = null;},1000);
|
||||
|
||||
const previouslyActiveEV = self.activeExcalidrawView;
|
||||
const newActiveviewEV: ExcalidrawView =
|
||||
leaf.view instanceof ExcalidrawView ? leaf.view : null;
|
||||
@@ -1497,7 +1542,9 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
if (previouslyActiveEV.leaf != leaf) {
|
||||
//if loading new view to same leaf then don't save. Excalidarw view will take care of saving anyway.
|
||||
//avoid double saving
|
||||
await previouslyActiveEV.save(true); //this will update transclusions in the drawing
|
||||
if(previouslyActiveEV.semaphores.dirty) {
|
||||
await previouslyActiveEV.save(true); //this will update transclusions in the drawing
|
||||
}
|
||||
}
|
||||
if (previouslyActiveEV.file) {
|
||||
self.triggerEmbedUpdates(previouslyActiveEV.file.path);
|
||||
@@ -1571,6 +1618,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
});
|
||||
}
|
||||
|
||||
//Save the drawing if the user clicks outside the canvas
|
||||
addFileSaveTriggerEventHandlers() {
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/551
|
||||
const onClickEventSaveActiveDrawing = (e: PointerEvent) => {
|
||||
@@ -1578,7 +1626,9 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
!this.activeExcalidrawView ||
|
||||
!this.activeExcalidrawView.semaphores.dirty ||
|
||||
//@ts-ignore
|
||||
e.target?.className === "excalidraw__canvas"
|
||||
e.target && (e.target.className === "excalidraw__canvas" ||
|
||||
//@ts-ignore
|
||||
getParentOfClass(e.target,"excalidraw-wrapper"))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -1675,9 +1725,6 @@ 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();
|
||||
@@ -1703,9 +1750,12 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
if (this.mathjaxDiv) {
|
||||
document.body.removeChild(this.mathjaxDiv);
|
||||
}
|
||||
//this.settings.drawingOpenCount += this.opencount;
|
||||
//this.settings.loadCount++;
|
||||
//this.saveSettings();
|
||||
|
||||
Object.values(this.packageMap).forEach((p:Packages)=>{
|
||||
delete p.excalidrawLib;
|
||||
delete p.reactDOM;
|
||||
delete p.react;
|
||||
})
|
||||
}
|
||||
|
||||
public async embedDrawing(file: TFile) {
|
||||
@@ -1779,30 +1829,38 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
|
||||
public triggerEmbedUpdates(filepath?: string) {
|
||||
const e = document.createEvent("Event");
|
||||
e.initEvent(RERENDER_EVENT, true, false);
|
||||
document
|
||||
.querySelectorAll(
|
||||
`div[class^='excalidraw-svg']${
|
||||
filepath ? `[src='${filepath.replaceAll("'", "\\'")}']` : ""
|
||||
}`,
|
||||
)
|
||||
.forEach((el) => el.dispatchEvent(e));
|
||||
const visitedDocs = new Set<Document>();
|
||||
app.workspace.iterateAllLeaves((leaf)=>{
|
||||
const ownerDocument = app.isMobile?document:leaf.view.containerEl.ownerDocument;
|
||||
if(!ownerDocument) return;
|
||||
if(visitedDocs.has(ownerDocument)) return;
|
||||
visitedDocs.add(ownerDocument);
|
||||
const e = ownerDocument.createEvent("Event");
|
||||
e.initEvent(RERENDER_EVENT, true, false);
|
||||
ownerDocument
|
||||
.querySelectorAll(
|
||||
`div[class^='excalidraw-svg']${
|
||||
filepath ? `[src='${filepath.replaceAll("'", "\\'")}']` : ""
|
||||
}`,
|
||||
)
|
||||
.forEach((el) => el.dispatchEvent(e));
|
||||
})
|
||||
}
|
||||
|
||||
public openDrawing(drawingFile: TFile, onNewPane: boolean) {
|
||||
let leaf: WorkspaceLeaf = null;
|
||||
|
||||
if (!leaf) {
|
||||
leaf = this.app.workspace.activeLeaf;
|
||||
public openDrawing(
|
||||
drawingFile: TFile,
|
||||
location: "active-pane"|"new-pane"|"popout-window"
|
||||
) {
|
||||
let leaf: WorkspaceLeaf;
|
||||
if(location === "popout-window") {
|
||||
//@ts-ignore
|
||||
leaf = app.workspace.openPopoutLeaf();
|
||||
}
|
||||
|
||||
if (!leaf) {
|
||||
leaf = this.app.workspace.getLeaf();
|
||||
}
|
||||
|
||||
if (onNewPane) {
|
||||
leaf = this.app.workspace.createLeafBySplit(leaf);
|
||||
else {
|
||||
leaf = this.app.workspace.getLeaf(false);
|
||||
if ((leaf.view.getViewType() !== 'empty') && (location === "new-pane")) {
|
||||
leaf = getNewOrAdjacentLeaf(this, leaf)
|
||||
}
|
||||
}
|
||||
|
||||
leaf.setViewState({
|
||||
@@ -1889,20 +1947,32 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
);
|
||||
await checkAndCreateFolder(this.app.vault, folderpath); //create folder if it does not exist
|
||||
const fname = getNewUniqueFilepath(this.app.vault, filename, folderpath);
|
||||
return await this.app.vault.create(
|
||||
const file = await this.app.vault.create(
|
||||
fname,
|
||||
initData ?? (await this.getBlankDrawing()),
|
||||
);
|
||||
|
||||
//wait for metadata cache
|
||||
let counter = 0;
|
||||
while(file instanceof TFile && !this.isExcalidrawFile(file) && counter++<10) {
|
||||
await sleep(50);
|
||||
}
|
||||
|
||||
if(counter > 10) {
|
||||
errorlog({file, error: "new drawing not recognized as an excalidraw file", fn: this.createDrawing});
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
public async createAndOpenDrawing(
|
||||
filename: string,
|
||||
onNewPane: boolean,
|
||||
location: "active-pane"|"new-pane"|"popout-window",
|
||||
foldername?: string,
|
||||
initData?: string,
|
||||
): Promise<string> {
|
||||
const file = await this.createDrawing(filename, foldername, initData);
|
||||
this.openDrawing(file, onNewPane);
|
||||
this.openDrawing(file, location);
|
||||
return file.path;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ReleaseNotes } from "../dialogs/ReleaseNotes";
|
||||
import { ScriptIconMap } from "../Scripts";
|
||||
import { getIMGFilename } from "../utils/FileUtils";
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
const dark = '<svg style="stroke:#ced4da;#212529;color:#ced4da;fill:#ced4da" ';
|
||||
const light = '<svg style="stroke:#212529;color:#212529;fill:#212529" ';
|
||||
|
||||
@@ -45,10 +46,12 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
|
||||
previousHeight: number = 0;
|
||||
onRightEdge: boolean = false;
|
||||
onBottomEdge: boolean = false;
|
||||
private containerRef = React.createRef<HTMLDivElement>();
|
||||
private containerRef: React.RefObject<HTMLDivElement>;
|
||||
|
||||
constructor(props: PanelProps) {
|
||||
super(props);
|
||||
const react = props.view.plugin.getPackage(props.view.ownerWindow).react;
|
||||
this.containerRef = react.createRef();
|
||||
this.state = {
|
||||
visible: props.visible,
|
||||
top: 50,
|
||||
@@ -191,7 +194,7 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
|
||||
: "none",
|
||||
height: "fit-content",
|
||||
maxHeight: "400px",
|
||||
zIndex: 3,
|
||||
zIndex: 5,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@@ -225,15 +228,15 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
|
||||
};
|
||||
|
||||
const onPointerUp = () => {
|
||||
document.removeEventListener("pointerup", onPointerUp);
|
||||
document.removeEventListener("pointermove", onDrag);
|
||||
this.props.view.ownerDocument?.removeEventListener("pointerup", onPointerUp);
|
||||
this.props.view.ownerDocument?.removeEventListener("pointermove", onDrag);
|
||||
};
|
||||
|
||||
event.preventDefault();
|
||||
this.penDownX = this.pos3 = event.clientX;
|
||||
this.penDownY = this.pos4 = event.clientY;
|
||||
document.addEventListener("pointerup", onPointerUp);
|
||||
document.addEventListener("pointermove", onDrag);
|
||||
this.props.view.ownerDocument.addEventListener("pointerup", onPointerUp);
|
||||
this.props.view.ownerDocument.addEventListener("pointermove", onDrag);
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
@@ -276,15 +279,10 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
|
||||
key={"release-notes"}
|
||||
title={t("READ_RELEASE_NOTES")}
|
||||
action={() => {
|
||||
const version: string =
|
||||
//@ts-ignore
|
||||
this.props.view.app.plugins.manifests[
|
||||
"obsidian-excalidraw-plugin"
|
||||
].version;
|
||||
new ReleaseNotes(
|
||||
this.props.view.app,
|
||||
this.props.view.plugin,
|
||||
version,
|
||||
PLUGIN_VERSION,
|
||||
).open();
|
||||
}}
|
||||
icon={ICONS.releaseNotes}
|
||||
@@ -520,13 +518,14 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
|
||||
)
|
||||
: this.state.scriptIconMap[key].name
|
||||
}
|
||||
action={() => {
|
||||
action={async () => {
|
||||
const f =
|
||||
this.props.view.app.vault.getAbstractFileByPath(key);
|
||||
if (f && f instanceof TFile) {
|
||||
this.props.view.plugin.scriptEngine.executeScript(
|
||||
this.props.view,
|
||||
f,
|
||||
await this.props.view.plugin.app.vault.read(f),
|
||||
this.props.view.plugin.scriptEngine.getScriptName(f)
|
||||
);
|
||||
}
|
||||
}}
|
||||
|
||||
116
src/settings.ts
116
src/settings.ts
@@ -44,6 +44,7 @@ export interface ExcalidrawSettings {
|
||||
zoomToFitOnResize: boolean;
|
||||
zoomToFitMaxLevel: number;
|
||||
openInAdjacentPane: boolean;
|
||||
openInMainWorkspace: boolean;
|
||||
showLinkBrackets: boolean;
|
||||
linkPrefix: string;
|
||||
urlPrefix: string;
|
||||
@@ -86,10 +87,21 @@ export interface ExcalidrawSettings {
|
||||
mdFontColor: string;
|
||||
mdBorderColor: string;
|
||||
mdCSS: string;
|
||||
scriptEngineSettings: {};
|
||||
scriptEngineSettings: {
|
||||
[key:string]: {
|
||||
[key:string]: {
|
||||
value?:string,
|
||||
hidden?: boolean,
|
||||
description?: string,
|
||||
valueset?: string[],
|
||||
height?: number,
|
||||
}
|
||||
}
|
||||
};
|
||||
defaultTrayMode: boolean;
|
||||
previousRelease: string;
|
||||
showReleaseNotes: boolean;
|
||||
mathjaxSourceURL: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
@@ -122,6 +134,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
hoverPreviewWithoutCTRL: false,
|
||||
linkOpacity: 1,
|
||||
openInAdjacentPane: false,
|
||||
openInMainWorkspace: true,
|
||||
showLinkBrackets: true,
|
||||
allowCtrlClick: true,
|
||||
forceWrap: false,
|
||||
@@ -169,6 +182,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
defaultTrayMode: false,
|
||||
previousRelease: "1.6.13",
|
||||
showReleaseNotes: true,
|
||||
mathjaxSourceURL: "https://cdn.jsdelivr.net/npm/mathjax@3.2.1/es5/tex-svg.js"
|
||||
};
|
||||
|
||||
const fragWithHTML = (html: string) =>
|
||||
@@ -178,6 +192,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
plugin: ExcalidrawPlugin;
|
||||
private requestEmbedUpdate: boolean = false;
|
||||
private requestReloadDrawings: boolean = false;
|
||||
private reloadMathJax: boolean = false;
|
||||
//private applyDebounceTimer: number = 0;
|
||||
|
||||
constructor(app: App, plugin: ExcalidrawPlugin) {
|
||||
@@ -218,6 +233,9 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
this.plugin.triggerEmbedUpdates();
|
||||
}
|
||||
this.plugin.scriptEngine.updateScriptPath();
|
||||
if(this.reloadMathJax) {
|
||||
this.plugin.loadMathJax();
|
||||
}
|
||||
}
|
||||
|
||||
async display() {
|
||||
@@ -529,7 +547,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
el.style.textAlign = "right";
|
||||
el.innerText = ` ${this.plugin.settings.zoomToFitMaxLevel.toString()}`;
|
||||
});
|
||||
|
||||
|
||||
this.containerEl.createEl("h1", { text: t("LINKS_HEAD") });
|
||||
this.containerEl.createEl(
|
||||
"span",
|
||||
@@ -545,12 +563,25 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
.setValue(this.plugin.settings.openInAdjacentPane)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.openInAdjacentPane = value;
|
||||
this.applySettingsUpdate(true);
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("MAINWORKSPACE_PANE_NAME"))
|
||||
.setDesc(fragWithHTML(t("MAINWORKSPACE_PANE_DESC")))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.openInMainWorkspace)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.openInMainWorkspace = value;
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("LINK_BRACKETS_NAME"))
|
||||
.setName(fragWithHTML(t("LINK_BRACKETS_NAME")))
|
||||
.setDesc(fragWithHTML(t("LINK_BRACKETS_DESC")))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
@@ -1098,6 +1129,24 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
}),
|
||||
);
|
||||
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/628
|
||||
new Setting(containerEl)
|
||||
.setName(t("MATHJAX_NAME"))
|
||||
.setDesc(t("MATHJAX_DESC"))
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown
|
||||
.addOption("https://cdn.jsdelivr.net/npm/mathjax@3.2.1/es5/tex-svg.js", "jsdelivr")
|
||||
.addOption("https://unpkg.com/mathjax@3.2.1/es5/tex-svg.js", "unpkg")
|
||||
.addOption("https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.1/es5/tex-svg-full.min.js","cdnjs")
|
||||
.setValue(this.plugin.settings.mathjaxSourceURL)
|
||||
.onChange((value)=> {
|
||||
this.plugin.settings.mathjaxSourceURL = value;
|
||||
this.reloadMathJax = true;
|
||||
this.applySettingsUpdate();
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
this.containerEl.createEl("h1", { text: t("EXPERIMENTAL_HEAD") });
|
||||
this.containerEl.createEl("p", { text: t("EXPERIMENTAL_DESC") });
|
||||
|
||||
@@ -1185,6 +1234,9 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
);
|
||||
});
|
||||
|
||||
//-------------------------------------
|
||||
//Script settings
|
||||
//-------------------------------------
|
||||
const scripts = this.plugin.scriptEngine
|
||||
.getListofScripts()
|
||||
?.map((f) => this.plugin.scriptEngine.getScriptName(f));
|
||||
@@ -1192,6 +1244,18 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
Object.keys(this.plugin.settings.scriptEngineSettings).length > 0 &&
|
||||
scripts
|
||||
) {
|
||||
const textAreaHeight = (scriptName: string, variableName: string): any => {
|
||||
const variable =
|
||||
//@ts-ignore
|
||||
this.plugin.settings.scriptEngineSettings[scriptName][variableName];
|
||||
switch (typeof variable) {
|
||||
case "object":
|
||||
return variable.height;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getValue = (scriptName: string, variableName: string): any => {
|
||||
const variable =
|
||||
//@ts-ignore
|
||||
@@ -1236,7 +1300,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
) => {
|
||||
new Setting(containerEl)
|
||||
.setName(variableName)
|
||||
.setDesc(description ?? "")
|
||||
.setDesc(fragWithHTML(description ?? ""))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(getValue(scriptName, variableName))
|
||||
@@ -1260,7 +1324,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
) {
|
||||
new Setting(containerEl)
|
||||
.setName(variableName)
|
||||
.setDesc(description ?? "")
|
||||
.setDesc(fragWithHTML(description ?? ""))
|
||||
.addDropdown((dropdown) => {
|
||||
valueset.forEach((val: any) =>
|
||||
dropdown.addOption(val.toString(), val.toString()),
|
||||
@@ -1273,17 +1337,33 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
new Setting(containerEl)
|
||||
.setName(variableName)
|
||||
.setDesc(description ?? "")
|
||||
.addText((text) =>
|
||||
text
|
||||
.setValue(getValue(scriptName, variableName))
|
||||
.onChange(async (value) => {
|
||||
setValue(scriptName, variableName, value);
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
if(textAreaHeight(scriptName, variableName)) {
|
||||
new Setting(containerEl)
|
||||
.setName(variableName)
|
||||
.setDesc(fragWithHTML(description ?? ""))
|
||||
.addTextArea((text) => {
|
||||
text.inputEl.style.minHeight = textAreaHeight(scriptName, variableName);
|
||||
text.inputEl.style.minWidth = "400px";
|
||||
text
|
||||
.setValue(getValue(scriptName, variableName))
|
||||
.onChange(async (value) => {
|
||||
setValue(scriptName, variableName, value);
|
||||
this.applySettingsUpdate();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
new Setting(containerEl)
|
||||
.setName(variableName)
|
||||
.setDesc(fragWithHTML(description ?? ""))
|
||||
.addText((text) =>
|
||||
text
|
||||
.setValue(getValue(scriptName, variableName))
|
||||
.onChange(async (value) => {
|
||||
setValue(scriptName, variableName, value);
|
||||
this.applySettingsUpdate();
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1294,7 +1374,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
) => {
|
||||
new Setting(containerEl)
|
||||
.setName(variableName)
|
||||
.setDesc(description ?? "")
|
||||
.setDesc(fragWithHTML(description ?? ""))
|
||||
.addText((text) =>
|
||||
text
|
||||
.setPlaceholder("Enter a number")
|
||||
|
||||
237
src/types.d.ts
vendored
Normal file
237
src/types.d.ts
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
import { ExcalidrawBindableElement, ExcalidrawElement, FileId, FillStyle, NonDeletedExcalidrawElement, StrokeSharpness, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { Point } from "@zsviczian/excalidraw/types/types";
|
||||
import { TFile, WorkspaceLeaf } from "obsidian";
|
||||
import { EmbeddedFilesLoader } from "./EmbeddedFileLoader";
|
||||
import { ExcalidrawAutomate } from "./ExcalidrawAutomate";
|
||||
import ExcalidrawView, { ExportSettings } from "./ExcalidrawView";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
|
||||
|
||||
export type ConnectionPoint = "top" | "bottom" | "left" | "right" | null;
|
||||
|
||||
export type Packages = {
|
||||
react: any,
|
||||
reactDOM: any,
|
||||
excalidrawLib: any,
|
||||
}
|
||||
|
||||
export interface ExcalidrawAutomateInterface {
|
||||
plugin: ExcalidrawPlugin;
|
||||
elementsDict: {[key:string]:any}; //contains the ExcalidrawElements currently edited in Automate indexed by el.id
|
||||
imagesDict: {[key: FileId]: any}; //the images files including DataURL, indexed by fileId
|
||||
style: {
|
||||
strokeColor: string; //https://www.w3schools.com/colors/default.asp
|
||||
backgroundColor: string;
|
||||
angle: number; //radian
|
||||
fillStyle: FillStyle; //type FillStyle = "hachure" | "cross-hatch" | "solid"
|
||||
strokeWidth: number;
|
||||
strokeStyle: StrokeStyle; //type StrokeStyle = "solid" | "dashed" | "dotted"
|
||||
roughness: number;
|
||||
opacity: number;
|
||||
strokeSharpness: StrokeSharpness; //type StrokeSharpness = "round" | "sharp"
|
||||
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
|
||||
startArrowHead: string; //"triangle"|"dot"|"arrow"|"bar"|null
|
||||
endArrowHead: string;
|
||||
};
|
||||
canvas: {
|
||||
theme: string; //"dark"|"light"
|
||||
viewBackgroundColor: string;
|
||||
gridSize: number;
|
||||
};
|
||||
getAPI(view?:ExcalidrawView):ExcalidrawAutomate;
|
||||
setFillStyle(val: number): void; //0:"hachure", 1:"cross-hatch" 2:"solid"
|
||||
setStrokeStyle(val: number): void; //0:"solid", 1:"dashed", 2:"dotted"
|
||||
setStrokeSharpness(val: number): void; //0:"round", 1:"sharp"
|
||||
setFontFamily(val: number): void; //1: Virgil, 2:Helvetica, 3:Cascadia
|
||||
setTheme(val: number): void; //0:"light", 1:"dark"
|
||||
addToGroup(objectIds: []): string;
|
||||
toClipboard(templatePath?: string): void;
|
||||
getElements(): ExcalidrawElement[]; //get all elements from ExcalidrawAutomate elementsDict
|
||||
getElement(id: string): ExcalidrawElement; //get single element from ExcalidrawAutomate elementsDict
|
||||
create(params?: {
|
||||
//create a drawing and save it to filename
|
||||
filename?: string; //if null: default filename as defined in Excalidraw settings
|
||||
foldername?: string; //if null: default folder as defined in Excalidraw settings
|
||||
templatePath?: string;
|
||||
onNewPane?: boolean;
|
||||
frontmatterKeys?: {
|
||||
"excalidraw-plugin"?: "raw" | "parsed";
|
||||
"excalidraw-link-prefix"?: string;
|
||||
"excalidraw-link-brackets"?: boolean;
|
||||
"excalidraw-url-prefix"?: string;
|
||||
};
|
||||
}): Promise<string>;
|
||||
createSVG(
|
||||
templatePath?: string,
|
||||
embedFont?: boolean,
|
||||
exportSettings?: ExportSettings, //use ExcalidrawAutomate.getExportSettings(boolean,boolean)
|
||||
loader?: EmbeddedFilesLoader, //use ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?)
|
||||
theme?: string,
|
||||
padding?: number
|
||||
): Promise<SVGSVGElement>;
|
||||
createPNG(
|
||||
templatePath?: string,
|
||||
scale?: number,
|
||||
exportSettings?: ExportSettings, //use ExcalidrawAutomate.getExportSettings(boolean,boolean)
|
||||
loader?: EmbeddedFilesLoader, //use ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?)
|
||||
theme?: string,
|
||||
): Promise<any>;
|
||||
wrapText(text: string, lineLen: number): string;
|
||||
addRect(topX: number, topY: number, width: number, height: number): string;
|
||||
addDiamond(topX: number, topY: number, width: number, height: number): string;
|
||||
addEllipse(topX: number, topY: number, width: number, height: number): string;
|
||||
addBlob(topX: number, topY: number, width: number, height: number): string;
|
||||
addText(
|
||||
topX: number,
|
||||
topY: number,
|
||||
text: string,
|
||||
formatting?: {
|
||||
wrapAt?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
textAlign?: string;
|
||||
box?: boolean | "box" | "blob" | "ellipse" | "diamond"; //if !null, text will be boxed
|
||||
boxPadding?: number;
|
||||
},
|
||||
id?: string,
|
||||
): string;
|
||||
addLine(points: [[x: number, y: number]]): string;
|
||||
addArrow(
|
||||
points: [[x: number, y: number]],
|
||||
formatting?: {
|
||||
startArrowHead?: string;
|
||||
endArrowHead?: string;
|
||||
startObjectId?: string;
|
||||
endObjectId?: string;
|
||||
},
|
||||
): string;
|
||||
addImage(topX: number, topY: number, imageFile: TFile): Promise<string>;
|
||||
addLaTex(topX: number, topY: number, tex: string): Promise<string>;
|
||||
connectObjects(
|
||||
objectA: string,
|
||||
connectionA: ConnectionPoint, //type ConnectionPoint = "top" | "bottom" | "left" | "right" | null
|
||||
objectB: string,
|
||||
connectionB: ConnectionPoint, //when passed null, Excalidraw will automatically decide
|
||||
formatting?: {
|
||||
numberOfPoints?: number; //points on the line. Default is 0 ie. line will only have a start and end point
|
||||
startArrowHead?: string; //"triangle"|"dot"|"arrow"|"bar"|null
|
||||
endArrowHead?: string; //"triangle"|"dot"|"arrow"|"bar"|null
|
||||
padding?: number;
|
||||
},
|
||||
): string;
|
||||
addLabelToLine(lineId: string, label:string): string;
|
||||
clear(): void; //clear elementsDict and imagesDict only
|
||||
reset(): void; //clear() + reset all style values to default
|
||||
isExcalidrawFile(f: TFile): boolean; //returns true if MD file is an Excalidraw file
|
||||
//view manipulation
|
||||
targetView: ExcalidrawView; //the view currently edited
|
||||
setView(view: ExcalidrawView | "first" | "active"): ExcalidrawView;
|
||||
getExcalidrawAPI(): any; //https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw#ref
|
||||
getViewElements(): ExcalidrawElement[]; //get elements in View
|
||||
deleteViewElements(el: ExcalidrawElement[]): boolean;
|
||||
getViewSelectedElement(): ExcalidrawElement; //get the selected element in the view, if more are selected, get the first
|
||||
getViewSelectedElements(): ExcalidrawElement[];
|
||||
getViewFileForImageElement(el: ExcalidrawElement): TFile | null; //Returns the TFile file handle for the image element
|
||||
copyViewElementsToEAforEditing(elements: ExcalidrawElement[]): void; //copies elements from view to elementsDict for editing
|
||||
viewToggleFullScreen(forceViewMode?: boolean): void;
|
||||
connectObjectWithViewSelectedElement( //connect an object to the selected element in the view
|
||||
objectA: string, //see connectObjects
|
||||
connectionA: ConnectionPoint,
|
||||
connectionB: ConnectionPoint,
|
||||
formatting?: {
|
||||
numberOfPoints?: number;
|
||||
startArrowHead?: string;
|
||||
endArrowHead?: string;
|
||||
padding?: number;
|
||||
},
|
||||
): boolean;
|
||||
addElementsToView( //Adds elements from elementsDict to the current view
|
||||
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>;
|
||||
registerThisAsViewEA():boolean;
|
||||
deregisterThisAsViewEA():boolean;
|
||||
onViewUnloadHook(view: ExcalidrawView): void;
|
||||
onViewModeChangeHook(isViewModeEnabled:boolean, view: ExcalidrawView, ea: ExcalidrawAutomate): void;
|
||||
onLinkHoverHook(
|
||||
element: NonDeletedExcalidrawElement,
|
||||
linkText: string,
|
||||
view: ExcalidrawView,
|
||||
ea: ExcalidrawAutomate
|
||||
):boolean;
|
||||
onLinkClickHook(
|
||||
element: ExcalidrawElement,
|
||||
linkText: string,
|
||||
event: MouseEvent,
|
||||
view: ExcalidrawView,
|
||||
ea: ExcalidrawAutomate
|
||||
): boolean;
|
||||
onDropHook(data: {
|
||||
//if set Excalidraw will call this function onDrop events
|
||||
ea: ExcalidrawAutomate;
|
||||
event: React.DragEvent<HTMLDivElement>;
|
||||
draggable: any; //Obsidian draggable object
|
||||
type: "file" | "text" | "unknown";
|
||||
payload: {
|
||||
files: TFile[]; //TFile[] array of dropped files
|
||||
text: string; //string
|
||||
};
|
||||
excalidrawFile: TFile; //the file receiving the drop event
|
||||
view: ExcalidrawView; //the excalidraw view receiving the drop
|
||||
pointerPosition: { x: number; y: number }; //the pointer position on canvas at the time of drop
|
||||
}): boolean; //a return of true will stop the default onDrop processing in Excalidraw
|
||||
mostRecentMarkdownSVG: SVGSVGElement; //Markdown renderer will drop a copy of the most recent SVG here for debugging purposes
|
||||
getEmbeddedFilesLoader(isDark?: boolean): EmbeddedFilesLoader; //utility function to generate EmbeddedFilesLoader object
|
||||
getExportSettings( //utility function to generate ExportSettings object
|
||||
withBackground: boolean,
|
||||
withTheme: boolean,
|
||||
): ExportSettings;
|
||||
getBoundingBox(elements: ExcalidrawElement[]): {
|
||||
//get bounding box of elements
|
||||
topX: number; //bounding box is the box encapsulating all of the elements completely
|
||||
topY: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
//elements grouped by the highest level groups
|
||||
getMaximumGroups(elements: ExcalidrawElement[]): ExcalidrawElement[][];
|
||||
//gets the largest element from a group. useful when a text element is grouped with a box, and you want to connect an arrow to the box
|
||||
getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;
|
||||
// Returns 2 or 0 intersection points between line going through `a` and `b`
|
||||
// and the `element`, in ascending order of distance from `a`.
|
||||
intersectElementWithLine(
|
||||
element: ExcalidrawBindableElement,
|
||||
a: readonly [number, number],
|
||||
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;
|
||||
isExcalidrawView(view: any): boolean;
|
||||
selectElementsInView(elements: ExcalidrawElement[]): void; //sets selection in view
|
||||
generateElementId(): string; //returns an 8 character long random id
|
||||
cloneElement(element: ExcalidrawElement): ExcalidrawElement; //Returns a clone of the element with a new id
|
||||
moveViewElementToZIndex(elementId: number, newZIndex: number): void; //Moves the element to a specific position in the z-index
|
||||
hexStringToRgb(color: string): number[];
|
||||
rgbToHexString(color: number[]): string;
|
||||
hslToRgb(color: number[]): number[];
|
||||
rgbToHsl(color: number[]): number[];
|
||||
colorNameToHex(color: string): string;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { normalizePath, TAbstractFile, TFolder, Vault } from "obsidian";
|
||||
import { normalizePath, Notice, TAbstractFile, TFile, TFolder, Vault } from "obsidian";
|
||||
import { ExcalidrawSettings } from "src/Settings";
|
||||
|
||||
/**
|
||||
@@ -122,10 +122,15 @@ export function getEmbedFilename(
|
||||
*/
|
||||
export async function checkAndCreateFolder(vault: Vault, folderpath: string) {
|
||||
folderpath = normalizePath(folderpath);
|
||||
const folder = vault.getAbstractFileByPath(folderpath);
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/658
|
||||
//@ts-ignore
|
||||
const folder = vault.getAbstractFileByPathInsensitive(folderpath);
|
||||
if (folder && folder instanceof TFolder) {
|
||||
return;
|
||||
}
|
||||
if (folder && folder instanceof TFile) {
|
||||
new Notice(`The folder cannot be created because it already exists as a file: ${folderpath}.`)
|
||||
}
|
||||
await vault.createFolder(folderpath);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { main } from "@popperjs/core";
|
||||
import {
|
||||
App,
|
||||
normalizePath, WorkspaceLeaf
|
||||
normalizePath, Notice, WorkspaceLeaf
|
||||
} from "obsidian";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { checkAndCreateFolder, splitFolderAndFilename } from "./FileUtils";
|
||||
@@ -14,52 +15,107 @@ export const getParentOfClass = (element: HTMLElement, cssClass: string):HTMLEle
|
||||
) {
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
return parent.classList.contains(cssClass) ? parent : null;
|
||||
return parent?.classList?.contains(cssClass) ? parent : null;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
| Setting | Originating Leaf |
|
||||
| | Main Workspace | Hover Editor | Popout Window |
|
||||
| ----------------------- | -------------------------------- | -------------------------------------- | -------------------------------- |
|
||||
| InMain && InAdjacent | 1.1 Reuse Leaf in Main Workspace | 1.1 Reuse Leaf in Main Workspace | 1.1 Reuse Leaf in Main Workspace |
|
||||
| InMain && !InAdjacent | 1.2 New Leaf in Main Workspace | 1.2 New Leaf in Main Workspace | 1.2 New Leaf in Main Workspace |
|
||||
| !InMain && InAdjacent | 1.1 Reuse Leaf in Main Workspace | 3 Reuse Leaf in Current Hover Editor | 4 Reuse Leaf in Current Popout |
|
||||
| !InMain && !InAdjacent | 1.2 New Leaf in Main Workspace | 2 New Leaf in Current Hover Editor | 2 New Leaf in Current Popout |
|
||||
*/
|
||||
|
||||
export const getNewOrAdjacentLeaf = (
|
||||
plugin: ExcalidrawPlugin,
|
||||
leaf: WorkspaceLeaf
|
||||
): WorkspaceLeaf => {
|
||||
const inHoverEditorLeaf = leaf.view?.containerEl
|
||||
? getParentOfClass(leaf.view.containerEl, "popover") !== null
|
||||
: false;
|
||||
//@ts-ignore
|
||||
const leafId = leaf.id;
|
||||
const layout = app.workspace.getLayout();
|
||||
const leafLoc =
|
||||
layout.main && layout.main.children.filter((x:any)=>x.type==="leaf" && x.id ===leafId).length > 0
|
||||
? "main"
|
||||
: layout.floating && layout.floating.children.filter((x:any)=>x.type==="leaf" && x.id ===leafId).length > 0
|
||||
? "popout"
|
||||
: layout.left && layout.left.children.filter((x:any)=>x.type==="leaf" && x.id ===leafId).length > 0
|
||||
? "left"
|
||||
: layout.right && layout.right.children.filter((x:any)=>x.type==="leaf" && x.id ===leafId).length > 0
|
||||
? "right"
|
||||
: "hover";
|
||||
|
||||
if (inHoverEditorLeaf) {
|
||||
const mainLeaves = app.workspace.getLayout().main.children.filter((c:any) => c.type === "leaf");
|
||||
if(mainLeaves.length === 0) {
|
||||
const getMainLeaf = ():WorkspaceLeaf => {
|
||||
let mainLeaf = app.workspace.getMostRecentLeaf();
|
||||
if(mainLeaf && mainLeaf !== leaf && mainLeaf.view?.containerEl.ownerDocument === document) {
|
||||
return mainLeaf;
|
||||
}
|
||||
mainLeaf = null;
|
||||
app.workspace.getLayout().main.children
|
||||
.filter((child:any)=>child.type==="leaf")
|
||||
.forEach((listItem:any)=> {
|
||||
const l = app.workspace.getLeafById(listItem.id);
|
||||
if(mainLeaf ||
|
||||
!l.view?.navigation ||
|
||||
leaf === l
|
||||
) return;
|
||||
mainLeaf = l;
|
||||
})
|
||||
return mainLeaf;
|
||||
}
|
||||
|
||||
//1
|
||||
if(plugin.settings.openInMainWorkspace || ["main","left","right"].contains(leafLoc)) {
|
||||
//1.1
|
||||
if(!plugin.settings.openInAdjacentPane) {
|
||||
if(leafLoc === "main") {
|
||||
return app.workspace.createLeafBySplit(leaf);
|
||||
}
|
||||
const ml = getMainLeaf();
|
||||
return ml
|
||||
? (ml.view.getViewType() === "empty" ? ml : app.workspace.createLeafBySplit(ml))
|
||||
: app.workspace.getLeaf(true);
|
||||
}
|
||||
|
||||
//1.2
|
||||
const ml = getMainLeaf();
|
||||
return ml ?? app.workspace.getLeaf(true);
|
||||
}
|
||||
|
||||
//2
|
||||
if(!plugin.settings.openInAdjacentPane) {
|
||||
return app.workspace.createLeafBySplit(leaf);
|
||||
}
|
||||
|
||||
//3
|
||||
if(leafLoc === "hover") {
|
||||
const leaves = new Set<WorkspaceLeaf>();
|
||||
app.workspace.iterateAllLeaves(l=>{
|
||||
//@ts-ignore
|
||||
return leafToUse = app.workspace.createLeafInParent(app.workspace.rootSplit);
|
||||
if(l!==leaf && leaf.containerEl.parentElement === l.containerEl.parentElement) leaves.add(l);
|
||||
})
|
||||
if(leaves.size === 0) {
|
||||
return plugin.app.workspace.createLeafBySplit(leaf);
|
||||
}
|
||||
const targetLeaf = app.workspace.getLeafById(mainLeaves[0].id);
|
||||
if (plugin.settings.openInAdjacentPane) {
|
||||
return targetLeaf;
|
||||
}
|
||||
return plugin.app.workspace.createLeafBySplit(targetLeaf);
|
||||
return Array.from(leaves)[0];
|
||||
}
|
||||
|
||||
if (plugin.settings.openInAdjacentPane) {
|
||||
let leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(
|
||||
leaf,
|
||||
"right"
|
||||
);
|
||||
if (!leafToUse) {
|
||||
leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(leaf, "left");
|
||||
//4
|
||||
if(leafLoc === "popout") {
|
||||
const popoutLeaves = new Set<WorkspaceLeaf>();
|
||||
app.workspace.iterateAllLeaves(l=>{
|
||||
if(l !== leaf && l.view.navigation && l.view.containerEl.ownerDocument === leaf.view.containerEl.ownerDocument) {
|
||||
popoutLeaves.add(l);
|
||||
}
|
||||
});
|
||||
if(popoutLeaves.size === 0) {
|
||||
return app.workspace.createLeafBySplit(leaf);
|
||||
}
|
||||
if (!leafToUse) {
|
||||
leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(
|
||||
leaf,
|
||||
"bottom"
|
||||
);
|
||||
}
|
||||
if (!leafToUse) {
|
||||
leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(leaf, "top");
|
||||
}
|
||||
if (!leafToUse) {
|
||||
leafToUse = plugin.app.workspace.createLeafBySplit(leaf);
|
||||
}
|
||||
return leafToUse;
|
||||
return Array.from(popoutLeaves)[0];
|
||||
}
|
||||
|
||||
return plugin.app.workspace.createLeafBySplit(leaf);
|
||||
};
|
||||
|
||||
@@ -78,7 +134,7 @@ export const getAttachmentsFolderAndFilePath = async (
|
||||
const activeFileFolder = `${splitFolderAndFilename(activeViewFilePath).folderpath}/`;
|
||||
folder = normalizePath(activeFileFolder + folder.substring(2));
|
||||
}
|
||||
if (!folder) {
|
||||
if (!folder || folder === "/") {
|
||||
folder = "";
|
||||
}
|
||||
await checkAndCreateFolder(app.vault, folder);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { exportToSvg, exportToBlob } from "@zsviczian/excalidraw";
|
||||
//import Excalidraw from "@zsviczian/excalidraw";
|
||||
import {
|
||||
App,
|
||||
Notice,
|
||||
@@ -23,6 +23,14 @@ import { ExportSettings } from "../ExcalidrawView";
|
||||
import { compressToBase64, decompressFromBase64 } from "lz-string";
|
||||
import { getIMGFilename } from "./FileUtils";
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
|
||||
const {
|
||||
exportToSvg,
|
||||
exportToBlob,
|
||||
//@ts-ignore
|
||||
} = excalidrawLib;
|
||||
|
||||
declare module "obsidian" {
|
||||
interface Workspace {
|
||||
getAdjacentLeafInDirection(
|
||||
@@ -41,8 +49,6 @@ export const checkExcalidrawVersion = async (app: App) => {
|
||||
return;
|
||||
}
|
||||
versionUpdateChecked = true;
|
||||
//@ts-ignore
|
||||
const manifest = app.plugins.manifests[PLUGIN_ID];
|
||||
|
||||
try {
|
||||
const gitAPIrequest = async () => {
|
||||
@@ -63,9 +69,9 @@ export const checkExcalidrawVersion = async (app: App) => {
|
||||
.filter((el: any) => el.version.match(/^\d+\.\d+\.\d+$/))
|
||||
.sort((el1: any, el2: any) => el2.published - el1.published)[0].version;
|
||||
|
||||
if (latestVersion > manifest.version) {
|
||||
if (isVersionNewerThanOther(latestVersion,PLUGIN_VERSION)) {
|
||||
new Notice(
|
||||
`A newer version of Excalidraw is available in Community Plugins.\n\nYou are using ${manifest.version}.\nThe latest is ${latestVersion}`,
|
||||
`A newer version of Excalidraw is available in Community Plugins.\n\nYou are using ${PLUGIN_VERSION}.\nThe latest is ${latestVersion}`,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -353,7 +359,7 @@ export const getImageSize = async (
|
||||
): Promise<{ height: number; width: number }> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve({ height: img.height, width: img.width });
|
||||
img.onload = () => resolve({ height: img.naturalHeight, width: img.naturalWidth });
|
||||
img.onerror = reject;
|
||||
img.src = src;
|
||||
});
|
||||
@@ -390,16 +396,23 @@ export const scaleLoadedImage = (
|
||||
};
|
||||
|
||||
export const setLeftHandedMode = (isLeftHanded: boolean) => {
|
||||
const newStylesheet = document.createElement("style");
|
||||
newStylesheet.id = "excalidraw-letf-handed";
|
||||
newStylesheet.textContent = `.excalidraw .App-bottom-bar{justify-content:flex-end;}`;
|
||||
const oldStylesheet = document.getElementById(newStylesheet.id);
|
||||
if (oldStylesheet) {
|
||||
document.head.removeChild(oldStylesheet);
|
||||
}
|
||||
if (isLeftHanded) {
|
||||
document.head.appendChild(newStylesheet);
|
||||
}
|
||||
const visitedDocs = new Set<Document>();
|
||||
app.workspace.iterateAllLeaves((leaf) => {
|
||||
const ownerDocument = app.isMobile?document:leaf.view.containerEl.ownerDocument;
|
||||
if(!ownerDocument) return;
|
||||
if(visitedDocs.has(ownerDocument)) return;
|
||||
visitedDocs.add(ownerDocument);
|
||||
const newStylesheet = ownerDocument.createElement("style");
|
||||
newStylesheet.id = "excalidraw-letf-handed";
|
||||
newStylesheet.textContent = `.excalidraw .App-bottom-bar{justify-content:flex-end;}`;
|
||||
const oldStylesheet = ownerDocument.getElementById(newStylesheet.id);
|
||||
if (oldStylesheet) {
|
||||
ownerDocument.head.removeChild(oldStylesheet);
|
||||
}
|
||||
if (isLeftHanded) {
|
||||
ownerDocument.head.appendChild(newStylesheet);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
export type LinkParts = {
|
||||
@@ -538,13 +551,26 @@ export const getPNGScale = (plugin: ExcalidrawPlugin, file: TFile): number => {
|
||||
return plugin.settings.pngExportScale;
|
||||
};
|
||||
|
||||
export const isVersionNewerThanOther = (version: string, otherVersion: string): boolean => {
|
||||
const v = version.match(/(\d*)\.(\d*)\.(\d*)/);
|
||||
const o = otherVersion.match(/(\d*)\.(\d*)\.(\d*)/);
|
||||
|
||||
return Boolean(v && v.length === 4 && o && o.length === 4 &&
|
||||
!(isNaN(parseInt(v[1])) || isNaN(parseInt(v[2])) || isNaN(parseInt(v[3]))) &&
|
||||
!(isNaN(parseInt(o[1])) || isNaN(parseInt(o[2])) || isNaN(parseInt(o[3]))) &&
|
||||
(
|
||||
parseInt(v[1])>parseInt(o[1]) ||
|
||||
(parseInt(v[1]) >= parseInt(o[1]) && parseInt(v[2]) > parseInt(o[2])) ||
|
||||
(parseInt(v[1]) >= parseInt(o[1]) && parseInt(v[2]) >= parseInt(o[2]) && parseInt(v[3]) > parseInt(o[3]))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export const errorlog = (data: {}) => {
|
||||
console.error({ plugin: "Excalidraw", ...data });
|
||||
};
|
||||
|
||||
export const sleep = async (ms: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
};
|
||||
export const sleep = async (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
export const log = console.log.bind(window.console);
|
||||
export const debug = console.log.bind(window.console);
|
||||
|
||||
3
testloader.js
Normal file
3
testloader.js
Normal file
File diff suppressed because one or more lines are too long
10
tsconfig-lib.json
Normal file
10
tsconfig-lib.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"outDir": "lib",
|
||||
"plugins": [{ "transform": "@zerollup/ts-transform-paths" }],
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["src/test/**/*", "lib/**/*"]
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"sourceMap": true,
|
||||
"module": "es2015",
|
||||
"target": "es2017",
|
||||
"allowJs": true,
|
||||
|
||||
24
tsconfig.prod.json
Normal file
24
tsconfig.prod.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"sourceMap": false,
|
||||
"module": "es2015",
|
||||
"target": "es2017",
|
||||
"allowJs": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"scripthost",
|
||||
"es2017",
|
||||
"esnext",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"jsx": "react"
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx", "src/Dialogs/OpenDrawing.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
{
|
||||
"1.6.24": "0.12.16",
|
||||
"1.7.12": "0.15.6",
|
||||
"1.7.8": "0.15.5",
|
||||
"1.7.7": "0.15.4",
|
||||
"1.7.6": "0.15.3",
|
||||
"1.7.2": "0.15.2",
|
||||
"1.6.34": "0.12.16",
|
||||
"1.4.2": "0.11.13"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user