Compare commits

...

17 Commits

Author SHA1 Message Date
zsviczian
76e2a32998 1.8.1 2022-11-29 20:19:12 +01:00
zsviczian
23a4f42c27 fix Black screen of death 2022-11-28 22:53:06 +01:00
zsviczian
8202cf0dde 1.8.1 beta 2022-11-27 23:10:36 +01:00
zsviczian
40f88bb900 Update README.md 2022-11-20 18:31:57 +01:00
zsviczian
e6f5f9469a 1.8.0 2022-11-20 17:59:42 +01:00
zsviczian
0502ac3bb0 Fix tools panel icon sizes, added save and open link actions, taskbone image size normalization 2022-11-19 21:31:11 +01:00
zsviczian
36125c9b83 fullscreen finish 2022-11-19 19:06:13 +01:00
zsviczian
cced2ca2e4 new-fullscreen mode 2022-11-19 18:51:37 +01:00
zsviczian
0de2c78cac added taskbone, fixed transclusion deconstruct 2022-11-19 18:23:59 +01:00
zsviczian
7848c8f705 1.7.30 2022-11-18 18:23:32 +01:00
zsviczian
f6c135227b rename to deconstruct 2022-11-15 23:42:50 +01:00
zsviczian
95ca41fcaa fix release notes 2022-11-15 21:21:09 +01:00
zsviczian
d7648d702a fixed release notes 2022-11-15 21:09:42 +01:00
zsviczian
5033a7cccf publish decompose script 2022-11-15 20:44:25 +01:00
zsviczian
1a83abb256 added decompose script image 2022-11-15 20:32:34 +01:00
zsviczian
08f616b5d9 1.7.29 2022-11-15 19:16:16 +01:00
zsviczian
ca44699e8d manifest-beta.json 2022-11-13 20:58:16 +01:00
28 changed files with 1072 additions and 311 deletions

337
README.md
View File

@@ -1,120 +1,275 @@
# Excalidraw
The Obsidian-Excalidraw plugin integrates [Excalidraw](https://excalidraw.com/), a feature rich sketching tool, into Obsidian. You can store and edit Excalidraw files in your vault, you can embed drawings into your documents, and you can link to documents and other drawings to/and from Excalidraw. For a showcase of Excalidraw features, please read my blog post [here](https://www.zsolt.blog/2021/03/showcasing-excalidraw.html) and/or watch the videos below.
Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
## Video Walkthrough
![image](https://user-images.githubusercontent.com/14358394/125159831-336d6880-e17a-11eb-8a3d-ceabc2555a08.png)
<a href="https://youtu.be/o0exK-xFP3k" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931370-aa4d88de-c4a8-46cc-aeb2-dc09aa0bea39.jpg" width="300"/></a>
# Video walkthrough
| | | |
|----|----|----|
|[![thumbnail](https://user-images.githubusercontent.com/14358394/156931370-aa4d88de-c4a8-46cc-aeb2-dc09aa0bea39.jpg)](https://youtu.be/o0exK-xFP3k)| | |
|[![Obsidian-Excalidraw 1.2.0 update - Major IMPROVEMENTS](https://user-images.githubusercontent.com/14358394/124356817-7b3f3d80-dc18-11eb-932d-363bb373c5ab.jpg)](https://youtu.be/UxJLLYtgDKE)|[![1 Getting Started](https://user-images.githubusercontent.com/14358394/125160304-7f211180-e17c-11eb-8363-c52723de1ffd.jpg)](https://youtu.be/sY4FoflGaiM)|[![2 Basic shapes and features](https://user-images.githubusercontent.com/14358394/125160312-8a743d00-e17c-11eb-9fa2-490ef4cbd59e.jpg)](https://youtu.be/Iy_oVTq12Gw)|
|[![3 Groups](https://user-images.githubusercontent.com/14358394/125160323-96f89580-e17c-11eb-9bce-8eb1067a51bb.jpg)](https://youtu.be/QOL1KF7-kdc)|[![4 Stencil](https://user-images.githubusercontent.com/14358394/125160332-9f50d080-e17c-11eb-98e9-fec60fe147d9.jpg)](https://youtu.be/aSgcbfspvfo)|[![5 embedding](https://user-images.githubusercontent.com/14358394/125160341-a546b180-e17c-11eb-9de8-d87fdc844c9c.jpg)](https://youtu.be/MaJ5jJwBRWs)|
|[![6 Links](https://user-images.githubusercontent.com/14358394/125160346-aa0b6580-e17c-11eb-930b-4024807040d1.jpg)](https://youtu.be/MXzeCOEExNo)|[![7 Markdown](https://user-images.githubusercontent.com/14358394/125160354-b2fc3700-e17c-11eb-81af-9e71e461f6dd.jpg)](https://youtu.be/R0IAg0s-wQE)|[![8 Templates](https://user-images.githubusercontent.com/14358394/125160360-b8f21800-e17c-11eb-8bd8-79d4e3f6e92d.jpg)](https://youtu.be/ibdS7ykwpW4)|
|[![9 Excalidraw Automate](https://user-images.githubusercontent.com/14358394/125160367-bdb6cc00-e17c-11eb-92f1-6f59faea85fd.jpg)](https://youtu.be/VRZVujfVab0)|[![10 Miscellaneous](https://user-images.githubusercontent.com/14358394/125160374-c3141680-e17c-11eb-8cc2-dfaffd903d15.jpg)](https://youtu.be/D1iBYo1_jjc)|[![Image Elements](https://user-images.githubusercontent.com/14358394/138607067-ccb62f92-48a4-4880-ac6e-68c1bf86ac2c.png)](https://www.youtube.com/watch?v=_c_0zpBJ4Xc&)|
|[![LaTex Demo](https://user-images.githubusercontent.com/14358394/143732412-1c65227e-4381-406d-847a-b001ab3506ca.jpg)](https://youtu.be/r08wk-58DPk)|[![markdown embeds](https://user-images.githubusercontent.com/14358394/143732440-90bfa029-8615-462e-ada3-c903d71a82c9.jpg)](https://youtu.be/tsecSfnTMow)|[![markdownAdvanced](https://user-images.githubusercontent.com/14358394/143783906-15cee494-c6d5-4495-a2ca-74634e4e7355.jpg)](https://youtu.be/K6qZkTz8GHs)|
|[![Script Engine](https://user-images.githubusercontent.com/14358394/145684531-8d9c2992-59ac-4ebc-804a-4cce1777ded2.jpg)](https://youtu.be/hePJcObHIso)|[![sticky notes thumbnail](https://user-images.githubusercontent.com/14358394/147283367-e5689385-ea51-4983-81a3-04d810d39f62.jpg)](https://youtu.be/NOuddK6xrr8)|[![plugin store](https://user-images.githubusercontent.com/14358394/147889174-6c306d0d-2d29-46cc-a53f-3f0013cf14de.jpg)](https://youtu.be/lzYdOQ6z8F0)|
|[![fourtfont](https://user-images.githubusercontent.com/14358394/149659524-2a4e0a24-40c9-4e66-a6b1-c92f3b88ecd5.jpg)](https://youtu.be/eKFmrSQhFA4)|[![thumbnail](https://user-images.githubusercontent.com/14358394/151705333-54e9ffd2-0bd7-4d02-b99e-0bd4e4708d4d.jpg)](https://youtu.be/qbPIAZguJeo)|[![Thumbnail](https://user-images.githubusercontent.com/14358394/152585752-7eb0371f-0bab-40f6-a194-3b48e5811735.jpg)](https://youtu.be/2Y8OhkGiTHg)|
|[![Thumbnail](https://user-images.githubusercontent.com/14358394/153676009-6f86b2d7-c248-49a2-b802-be21c6999e4f.jpg)](https://youtu.be/2v9TZmQNO8c)|[![Thumbnail](https://user-images.githubusercontent.com/14358394/154821232-a404b6cf-72fb-4ce4-9d53-619132dce491.jpg)](https://youtu.be/xHPGWR3m0c8)|[![Thumbnail](https://user-images.githubusercontent.com/14358394/156931428-b2269fd9-87bd-43ab-8558-5572f40dff93.jpg)](https://youtu.be/gMIKXyhS-dM)|
|[![thumbnail](https://user-images.githubusercontent.com/14358394/156931461-0979b821-315a-41dd-86f1-31d169b7c127.jpg)](https://youtu.be/Etskjw7a5zo)|[![Thumbnail](https://user-images.githubusercontent.com/14358394/158008902-12c6a851-237e-4edd-a631-d48e81c904b2.jpg)](https://youtu.be/4N6efq1DtH0)|[![thumbnail](https://user-images.githubusercontent.com/14358394/159369910-6371f08d-b5fa-454d-9c6c-948f7e7a7d26.jpg)](https://youtu.be/U2LkBRBk4LY)|
| [![6 strategies for linking your visual thoughts v4](https://user-images.githubusercontent.com/14358394/171635214-30533c45-94fa-436e-83a9-b2ec99f190e2.jpg)](https://youtu.be/qiKuqMcNWgU)|[![Video thumbnail small](https://user-images.githubusercontent.com/14358394/185791706-3d9983ab-7cb1-4b27-a016-30c039d84e34.jpg)](https://youtu.be/yZQoJg2RCKI)|[![Thumbnail - Colors - Excalidraw Basics (Custom)](https://user-images.githubusercontent.com/14358394/194773147-5418a0ab-6be5-4eb0-a8e4-d6af21b1b483.png)](https://youtu.be/6PLGHBH9VZ4) |
|[![Thumbnail - Excalidraw color palettes (Custom)](https://user-images.githubusercontent.com/14358394/194773211-9e871be7-0795-4dc7-947e-c6c275e690d0.png)](https://youtu.be/epYNx2FSf2w) | [![Thumbnail (Custom)](https://user-images.githubusercontent.com/14358394/194773268-400cfb1b-6bde-45e0-9e4b-91bbaa461cf0.png)](https://youtu.be/Amhlv6r9WvM) | [![Thumbnail - Simple rules for beautiful sketches (Custom) (1)](https://user-images.githubusercontent.com/14358394/194773527-ef35c8b9-1a6d-4415-9c7e-b667fb17535d.png)](https://youtu.be/r9oB1SlK1GU) |
|[![Thumbnail - ColorMaster Scripting (Custom)](https://user-images.githubusercontent.com/14358394/195988535-a133a9b9-d094-45ba-ba64-c994b9a1e0ef.png)](https://youtu.be/7gJDwNgQ6NU) | [![Thumbnail - 1 7 27 SVG import (Custom)](https://user-images.githubusercontent.com/14358394/199207784-8bbe14e0-7d10-47d7-971d-20dce8dbd659.png)](https://youtu.be/vlC1-iBvIfo) | |
<details><summary>10 Part (slightly outdated) Video Walkthrough</summary>
<a href="https://youtu.be/sY4FoflGaiM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160304-7f211180-e17c-11eb-8363-c52723de1ffd.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;1 Getting Started</a><br>
<a href="https://youtu.be/Iy_oVTq12Gw" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160312-8a743d00-e17c-11eb-9fa2-490ef4cbd59e.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;2 Basic shapes and features</a><br>
<a href="https://youtu.be/QOL1KF7-kdc" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160323-96f89580-e17c-11eb-9bce-8eb1067a51bb.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;3 Grouping elements</a><br>
<a href="https://youtu.be/aSgcbfspvfo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160332-9f50d080-e17c-11eb-98e9-fec60fe147d9.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;4 The stencil-library</a><br>
<a href="https://youtu.be/MaJ5jJwBRWs" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160341-a546b180-e17c-11eb-9de8-d87fdc844c9c.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;5 Embedding</a><br>
<a href="https://youtu.be/MXzeCOEExNo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160346-aa0b6580-e17c-11eb-930b-4024807040d1.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;6 Links</a><br>
<a href="https://youtu.be/R0IAg0s-wQE" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160354-b2fc3700-e17c-11eb-81af-9e71e461f6dd.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;7 Markdown</a><br>
<a href="https://youtu.be/ibdS7ykwpW4" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160360-b8f21800-e17c-11eb-8bd8-79d4e3f6e92d.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;8 Templates</a><br>
<a href="https://youtu.be/VRZVujfVab0" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160367-bdb6cc00-e17c-11eb-92f1-6f59faea85fd.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;9 Excalidraw Automate</a><br>
<a href="https://youtu.be/D1iBYo1_jjc" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160374-c3141680-e17c-11eb-8cc2-dfaffd903d15.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;10 Miscellaneous</a><br>
</details>
<details><summary>Embedding stuff into Excalidraw</summary>
<a href="https://www.youtube.com/watch?v=_c_0zpBJ4Xc&" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/138607067-ccb62f92-48a4-4880-ac6e-68c1bf86ac2c.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Image Elements</a><br>
<a href="https://youtu.be/r08wk-58DPk" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/143732412-1c65227e-4381-406d-847a-b001ab3506ca.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;LaTex Demo</a><br>
<a href="https://youtu.be/tsecSfnTMow" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/143732440-90bfa029-8615-462e-ada3-c903d71a82c9.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Markdown embeds</a><br>
<a href="https://youtu.be/K6qZkTz8GHs" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/143783906-15cee494-c6d5-4495-a2ca-74634e4e7355.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Markdown embeds advanced features</a><br>
<a href="https://youtu.be/Etskjw7a5zo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931461-0979b821-315a-41dd-86f1-31d169b7c127.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Link to Elements, Vertical text alignment, Markdown Styling</a><br>
</details>
<details><summary>The Script Engine Store - Excalidraw Automation</summary>
<a href="https://youtu.be/hePJcObHIso" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/145684531-8d9c2992-59ac-4ebc-804a-4cce1777ded2.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Introducing the Script Engine</a><br>
<a href="https://youtu.be/lzYdOQ6z8F0" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/147889174-6c306d0d-2d29-46cc-a53f-3f0013cf14de.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Script Enginge Store</a><br>
</details>
<details><summary>Working with colors</summary>
<a href="https://youtu.be/6PLGHBH9VZ4" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/194773147-5418a0ab-6be5-4eb0-a8e4-d6af21b1b483.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Colors - Excalidraw Basics (Custom)</a><br>
<a href="https://youtu.be/epYNx2FSf2w" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/194773211-9e871be7-0795-4dc7-947e-c6c275e690d0.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Excalidraw color palettes (Custom)</a><br>
<a href="https://youtu.be/Amhlv6r9WvM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/194773268-400cfb1b-6bde-45e0-9e4b-91bbaa461cf0.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;"Artistic" Color Gradients</a><br>
<a href="https://youtu.be/r9oB1SlK1GU" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/194773527-ef35c8b9-1a6d-4415-9c7e-b667fb17535d.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Simple rules for beautiful sketches</a><br>
<a href="https://youtu.be/7gJDwNgQ6NU" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/195988535-a133a9b9-d094-45ba-ba64-c994b9a1e0ef.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;ColorMaster Scripting</a><br>
</details>
<details><summary>Links and block references</summary>
<a href="https://youtu.be/qiKuqMcNWgU" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/171635214-30533c45-94fa-436e-83a9-b2ec99f190e2.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;6 strategies for linking your visual thoughts v4</a><br>
<a href="https://youtu.be/yZQoJg2RCKI" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/185791706-3d9983ab-7cb1-4b27-a016-30c039d84e34.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Block reference parts of images</a><br>
<a href="https://youtu.be/Etskjw7a5zo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931461-0979b821-315a-41dd-86f1-31d169b7c127.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Link to Elements, Vertical text alignment, Markdown Styling</a><br>
<a href="https://youtu.be/2Y8OhkGiTHg" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/152585752-7eb0371f-0bab-40f6-a194-3b48e5811735.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;How to guide for the Excalidraw-native hyperlinks</a><br>
</details>
<details><summary>Powertools</summary>
<a href="https://youtu.be/NOuddK6xrr8" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/147283367-e5689385-ea51-4983-81a3-04d810d39f62.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Sticky Notes (word wrapping)</a><br>
<a href="https://youtu.be/eKFmrSQhFA4" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/149659524-2a4e0a24-40c9-4e66-a6b1-c92f3b88ecd5.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Fourt Font</a><br>
<a href="https://youtu.be/vlC1-iBvIfo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/199207784-8bbe14e0-7d10-47d7-971d-20dce8dbd659.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;SVG import</a><br>
<a href="https://youtu.be/7gu4ETx7zro" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/202916770-28f2fa64-1ba2-4b40-a7fe-d721b42634f7.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;OCR</a><br>
<a href="https://youtu.be/U2LkBRBk4LY" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/159369910-6371f08d-b5fa-454d-9c6c-948f7e7a7d26.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Bind/unbind text from container, Frontmatter tags to customize export</a><br>
</details>
<details><summary>Quality of life improvements</summary>
<a href="https://youtu.be/qbPIAZguJeo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/151705333-54e9ffd2-0bd7-4d02-b99e-0bd4e4708d4d.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Mobile Support</a><br>
<a href="https://youtu.be/2v9TZmQNO8c" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/153676009-6f86b2d7-c248-49a2-b802-be21c6999e4f.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Tray-mode and Customizable Color Palette</a><br>
<a href="https://youtu.be/xHPGWR3m0c8" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/154821232-a404b6cf-72fb-4ce4-9d53-619132dce491.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Compressed JSON and improved save/sync support</a><br>
<a href="https://youtu.be/gMIKXyhS-dM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931428-b2269fd9-87bd-43ab-8558-5572f40dff93.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;The Obsidian Tools Panel</a><br>
<a href="https://youtu.be/4N6efq1DtH0" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/158008902-12c6a851-237e-4edd-a631-d48e81c904b2.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Eraser, left-handed mode, improved filename configuration</a><br>
</details>
---
## Features
# Key features
- The plugin integrates Excalidraw seamlessly into Obsidian including Command Palette actions, File Explorer features, Option Menu commands, and the Ribbon Button.
- <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.
- Via the template you can customize the color palette used by Excalidraw. Switch to Markdown view. Scroll down to the bottom of the file and find `"AppState": {`. Find `"customColorPalette": {` at the end of the AppState section. You may specify the 3 palettes used in Excalidraw by adding any or all of the following 3 variables: `"canvasBackground":[], "elementBackground":[], "elementStroke": []`. Add a comma separated list of valid HTML colors (e.g. `#FF0000` for red) in the array for each of the variables. See my videos above for further help.
- 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.
- Enable / disable autosave.
- You can customize the size and position of the embedded images using the `![[image.excalidraw|100]]`, `![[image.excalidraw|100x100]]`, `![[image.excalidraw|100|left]]`, `![[image.excalidraw|right-wrap]]`, formatting options. `![[<filename.excalidraw>|<width>x<height>|<alignment>]]`. You can add your custom [alignment via CSS](https://www.scaler.com/topics/align-image-in-html/). Any text that appears in `<alignment>` will be added to the rendered SVG element style and to the wrapper DIV element. See [styles.css](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/styles.css) for more insight.
- Supports hyperlinks e.g. `https://zsolt.blog`, `[Obsidian](https://obsidian.md)`, and internal links e.g. `[[My file in vault|Alias]]` in drawing text.
- Links will update when files are moved or renamed, if you have the Obsidian setting Files & Links/Automatically Update Internal Links enabled.
- Links in drawings will show up in backlinks of documents
- Transclusions are supported
- The plugin integrates Excalidraw seamlessly into Obsidian including Command Palette actions, File
Explorer features, Option Menu commands, and the Ribbon Button.
- <kbd>CTRL/CMD+Click</kbd> on the ribbon button, or in the file explorer to create / open drawings
in a new pane.
### Settings
Settings will allow you to customize Excalidraw to your needs:
- Default folder for new drawings and define custom filename pattern for new drawings.
#### Templates
- 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.
- Via the template you can customize the color palette used by Excalidraw.
- Switch to Markdown view.
- Scroll down to the bottom of the file and find `"AppState": {`.
- Find `"customColorPalette": {` at the end of the AppState section.
- You may specify the 3 palettes used in Excalidraw by adding any or all of the following 3 variables:
- `"canvasBackground":[], "elementBackground":[], "elementStroke": []`.
- Add a comma separated list of valid HTML colors (e.g. `#FF0000` for red)
in the array for each of the variables.
- See my videos above for further help.
#### Export
- 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.
- Enable / disable autosave.
### Embedded images
- You can customize the size and position of the embedded images using the
- `![[image.excalidraw|100]]`,
- `![[image.excalidraw|100x100]]`,
- `![[image.excalidraw|100|left]]`,
- `![[image.excalidraw|right-wrap]]`, formatting options.
- `![[<filename.excalidraw>|<width>x<height>|<alignment>]]`.
- You can add your custom [alignment via CSS](https://www.scaler.com/topics/align-image-in-html/).
- Any text that appears in `<alignment>` will be added to the rendered SVG element style and to the wrapper DIV element.
- See [styles.css](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/styles.css) for more insight.
### Hyperlinks
- Supports hyperlinks e.g.
- `https://zsolt.blog`,
- `[Obsidian](https://obsidian.md)`, and
- internal links e.g. `[[My file in vault|Alias]]` in drawing text.
- Links will update when files are moved or renamed, if you have the Obsidian
setting Files & Links/Automatically Update Internal Links enabled.
- Links in drawings will show up in backlinks of documents
- Transclusions are supported
- `![[myfile#^blockref]]` will convert in the drawing into the transcluded text of the block
- `![[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
- <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
- 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
- <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 <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, ICO, GIF, WEBP, Excalidraw) from Obsidian's file explorer while pressing the <kbd>CTRL</kbd> (<kbd>SHIFT</kbd> on Mac) button will embed the image into your drawing.
- If in addition to <kbd>CTRL</kbd> or <kbd>SHIFT</kbd> you also hold down <kbd>ALT<kbd>, the image will be inserted at 100% of its size. ⚠ Note: this is a very niche feature with a very particular behavior that I built primarily for myself (even more so than other features in Excalidraw Obsidian - also built primarily for myself 😉)... This will reset your embedded image to 100% size every time you open the Excalidraw drawing, or in case you have embedded an Excalidraw drawing on your canvas inserted using this function, every time you update the embedded drawing, it will be scaled back to 100% size. This means that even if you resize the image on the drawing, it will reset to 100% the next time you open the file or you modify the original embedded object. This feature is useful when you decompose a drawing into separate Excalidraw files, but when combined onto a single canvas you want the individual pieces to maintain their actual sizes. I use this feature to construct Book-on-a-Page summaries from atomic drawings.
- 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
- On iOS and Android you can add images from your camera by pressing the add image button in Excalidraw.
- You can copy/paste images into your drawing. Images will be saved in your vault.
- You can drag and drop images as explained above.
- Block referencing parts of images
- When referencing an element on the canvas in a link pointing to an Excalidraw file using the elementId or the section header (i.e. a Text Element containing the `# <Section title>`) - e.g. `[[file#^elementID]]`, you can add the `group=` prefix, e.g. `[[file#^group=elementID]]` or the `area=` prefix, e.g. `[[file#area=Section heading]]`.
- If the `group=` prefix is found Excalidraw will select the group of elements in the same group as the element referenced by the elementID (block reference) or the section heading.
### LaTeX
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, ICO, GIF, WEBP, Excalidraw) from Obsidian's file explorer while pressing the
<kbd>CTRL</kbd> (<kbd>SHIFT</kbd> on Mac) button will embed the image into your drawing.
- If in addition to <kbd>CTRL</kbd> or <kbd>SHIFT</kbd> you also hold down <kbd>ALT</kbd>,
the image will be inserted at 100% of its size.
- Note: this is a very niche feature with a very particular behavior that I built primarily for myself
- (even more so than other features in Excalidraw Obsidian - also built primarily for myself 😉).
- This will reset your embedded image to 100% size every time you open the Excalidraw drawing,
or in case you have embedded an Excalidraw drawing on your canvas inserted using this function,
every time you update the embedded drawing, it will be scaled back to 100% size.
- This means that even if you resize the image on the drawing, it will reset to 100% the next time you open
the file or you modify the original embedded object. This feature is useful when you
decompose a drawing into separate Excalidraw files, but when combined onto a single canvas
you want the individual pieces to maintain their actual sizes. I use this feature to
construct Book-on-a-Page summaries from atomic drawings.
- 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
- On iOS and Android you can add images from your camera by pressing the add image button in Excalidraw.
- You can copy/paste images into your drawing. Images will be saved in your vault.
- You can drag and drop images as explained above.
### Block referencing parts of images
- When referencing an element on the canvas in a link pointing to an Excalidraw file using
- the elementId or the section header (i.e. a Text Element containing the `# <Section title>`)
- e.g. `[[file#^elementID]]`,
- you can add the `group=` prefix,
- e.g. `[[file#^group=elementID]]` or
- the `area=` prefix,
- e.g. `[[file#area=Section heading]]`.
- If the `group=` prefix is found Excalidraw will select the group of elements in the
same group as the element referenced by the elementID (block reference) or the section heading.
- If the `area=` prefix is found Excalidraw will insert a cutout of the image around the referenced element.
- Note that the `area=` selector is not supported when embedding Excalidraw as PNG into your markdown documents.
- Referencing the elementID of a text element without the `group=` or `area=` prefix will transclude the element as plain text. Referencing a non-Text Element (e.g. rectangle, ellipse, etc.) without the `group=` or `area=` prefix will result in an Obsidian error since these elementIds are not present in the Excalidraw markdown file as block references.
- Referencing the elementID of a text element without the `group=` or `area=` prefix will
transclude the element as plain text. Referencing a non-Text Element (e.g. rectangle,
ellipse, etc.) without the `group=` or `area=` prefix will result in an Obsidian error
since these elementIds are not present in the Excalidraw markdown file as block
references.
### Markdown
- Since 1.2.0 Drawing files are stored in Markdown files
- You can add tags to drawings
- You can add metadata to the YAML front matter of drawings
- Anything you add between the frontmatter and the `# Text Elements` heading will be ignored by Excalidraw, i.e. you can add whatever you like here, it will be preserved as part of the document.
- Excalidraw documents now show in graph view.
- The following front matter keys will customize how the drawing is displayed - overriding general settings:
- `excalidraw-link-prefix: "📍"` preview prefix for internal links
- `excalidraw-url-prefix: "🌐"` preview prefix for external links
- `excalidraw-link-brackets: true|false` whether or not to display brackets around links in preview
- `excalidraw-default-mode: view|zen` Open this document in view mode or zen mode by defult. Default view mode is excellent for presentation slides.
- Frontmatter tags to customize image export at a file level [519](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/519). If these keys are present they will override the default excalidraw embed and export settings.
- `excalidraw-export-transparent: true`: true == Transparent / false == with background.
- `excalidraw-export-dark`: true == Dark mode / false == light mode.
- `excalidraw-export-padding`: 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 <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 (<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:
- You can add tags to drawings
- You can add metadata to the YAML front matter of drawings
- Anything you add between the frontmatter and the `# Text Elements` heading will be ignored by Excalidraw, i.e. you can add whatever you like here, it will be preserved as part of the document.
- Excalidraw documents now show in graph view.
- The following front matter keys will customize how the drawing is displayed - overriding general settings:
- `excalidraw-link-prefix: "📍"` preview prefix for internal links
- `excalidraw-url-prefix: "🌐"` preview prefix for external links
- `excalidraw-link-brackets: true|false` whether or not to display brackets around links in preview
- `excalidraw-default-mode: view|zen` Open this document in view mode or zen mode by defult. Default view mode is excellent for presentation slides.
- Frontmatter tags to customize image export at a file level [519](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/519). If these keys are present they will override the default excalidraw embed and export settings.
- `excalidraw-export-transparent: true`: true == Transparent / false == with background.
- `excalidraw-export-dark`: true == Dark mode / false == light mode.
- `excalidraw-export-padding`: 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 <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 (<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-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 <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).
- 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]]`
### Other
- 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.
# 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)
---
Please head over to [GitHub](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues) to report a bug or request an enhancement.
## Feedback, questions, ideas, problems
# Say Thank You
If you are enjoying Excalidraw then please support my work and enthusiasm by buying me a coffee on [https://ko-fi/zsolt](https://ko-fi.com/zsolt).
Join the conversation about the Excalidraw plugin on
[forum.obsidian.md](https://forum.obsidian.md/t/excalidraw-full-featured-sketching-plugin-in-obsidian)
Please also help spread the word by sharing about the Obsidian Excalidraw Plugin on Twitter, Reddit, or any other social media platform you regularly use.
Please head over to [GitHub](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues) to
report a bug or request an enhancement.
---
## Say Thank You
If you are enjoying Excalidraw then please support my work and enthusiasm by buying me a coffee on
[https://ko-fi/zsolt](https://ko-fi.com/zsolt).
Please also help spread the word by sharing about the Obsidian Excalidraw Plugin on Twitter, Reddit,
or any other social media platform you regularly use.
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
---
## Friends of Excalidraw
If you enjoy Excalidraw, consider giving [ExcaliBrain](https://github.com/zsviczian/excalibrain) a try (also available via Obsidian Community Plugins).
[![thumbnail](https://user-images.githubusercontent.com/14358394/169708346-9e41289d-9536-43ec-8f70-2d2ad2d369d6.png)](https://youtu.be/gOkniMkDPyM)
<a href="https://youtu.be/gOkniMkDPyM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/169708346-9e41289d-9536-43ec-8f70-2d2ad2d369d6.png" width="300"/></a>

View File

@@ -0,0 +1,71 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-deconstruct.jpg)
Select some elements in the scene. The script will take these elements and move them into a new Excalidraw file, and open that file. The selected elements will also be replaced in your original drawing with the embedded Excalidraw file (the one that was just created). You will be prompted for the file name of the new deconstructed image. The script is useful if you want to break a larger drawing into smaller reusable parts that you want to reference in multiple drawings.
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.7.29")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
const els = ea.getViewSelectedElements();
if (els.length === 0) {
new Notice("You must select elements first")
return;
}
const bb = ea.getBoundingBox(els);
ea.copyViewElementsToEAforEditing(els);
ea.getElements().filter(el=>el.type==="image").forEach(el=>{
const img = ea.targetView.excalidrawData.getFile(el.fileId);
const path = (img?.linkParts?.original)??(img?.file?.path);
if(img && path) {
ea.imagesDict[el.fileId] = {
mimeType: img.mimeType,
id: el.fileId,
dataURL: img.img,
created: img.mtime,
file: path,
hasSVGwithBitmap: img.isSVGwithBitmap,
latex: null,
};
return;
}
const equation = ea.targetView.excalidrawData.getEquation(el.fileId);
eqImg = ea.targetView.getScene()?.files[el.fileId]
if(equation && eqImg) {
ea.imagesDict[el.fileId] = {
mimeType: eqImg.mimeType,
id: el.fileId,
dataURL: eqImg.dataURL,
created: eqImg.created,
file: null,
hasSVGwithBitmap: null,
latex: equation.latex,
};
return;
}
});
let folder = ea.targetView.file.path;
folder = folder.lastIndexOf("/")===-1?"":folder.substring(0,folder.lastIndexOf("/"))+"/";
const fname = await utils.inputPrompt("Filename for new file","Filename","");
const template = app.metadataCache.getFirstLinkpathDest(ea.plugin.settings.templateFilePath,"");
const newPath = await ea.create ({
filename: fname + ".md",
foldername: folder,
templatePath: template?.path,
onNewPane: true
});
setTimeout(async ()=>{
const file = app.metadataCache.getFirstLinkpathDest(newPath,"")
ea.deleteViewElements(els);
ea.clear();
await ea.addImage(bb.topX,bb.topY,file,false);
ea.addElementsToView(false, true, true);
},1000);

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M169.7 .9c-22.8-1.6-41.9 14-47.5 34.7L110.4 80c.5 0 1.1 0 1.6 0c176.7 0 320 143.3 320 320c0 .5 0 1.1 0 1.6l44.4-11.8c20.8-5.5 36.3-24.7 34.7-47.5C498.5 159.5 352.5 13.5 169.7 .9zM399.8 410.2c.1-3.4 .2-6.8 .2-10.2c0-159.1-128.9-288-288-288c-3.4 0-6.8 .1-10.2 .2L.5 491.9c-1.5 5.5 .1 11.4 4.1 15.4s9.9 5.6 15.4 4.1L399.8 410.2zM176 272c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32zm128 64c0 17.7-14.3 32-32 32s-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32zM160 384c0 17.7-14.3 32-32 32s-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32z"/></svg>

After

Width:  |  Height:  |  Size: 624 B

File diff suppressed because one or more lines are too long

View File

@@ -41,6 +41,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/Copy%20Selected%20Element%20Styles%20to%20Global.svg"/></div>|[[#Copy Selected Element Styles to Global]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Create%20new%20markdown%20file%20and%20embed%20into%20active%20drawing.svg"/></div>|[[#Create new markdown file and embed into active drawing]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Darken%20background%20color.svg"/></div>|[[#Darken background color]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Deconstruct%20selected%20elements%20into%20new%20drawing.svg"/></div>|[[#Deconstruct selected elements into new drawing]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Elbow%20connectors.svg"/></div>|[[#Elbow connectors]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20horizontally%20keep%20text%20centered.svg"/></div>|[[#Expand rectangles horizontally keep text centered]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20horizontally.svg"/></div>|[[#Expand rectangles horizontally]]|
@@ -160,6 +161,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/Darken%20background%20color.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script darkens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect. In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png'></td></tr></table>
## Deconstruct selected elements into new drawing
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Deconstruct%20selected%20elements%20into%20new%20drawing.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/Deconstruct%20selected%20elements%20into%20new%20drawing.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Select some elements in the scene. The script will take these elements and move them into a new Excalidraw file, and open that file. The selected elements will also be replaced in your original drawing with the embedded Excalidraw file (the one that was just created). You will be prompted for the file name of the new deconstructed image. The script is useful if you want to break a larger drawing into smaller reusable parts that you want to reference in multiple drawings.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-deconstruct.jpg'></td></tr></table>
## Elbow connectors
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Elbow%20connectors.md

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

10
manifest-beta.json Normal file
View File

@@ -0,0 +1,10 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "1.7.28-beta",
"minAppVersion": "0.15.6",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",
"authorUrl": "https://zsolt.blog",
"isDesktopOnly": false
}

View File

@@ -1,8 +1,8 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "1.7.27",
"minAppVersion": "0.15.6",
"version": "1.8.1",
"minAppVersion": "0.16.0",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",
"authorUrl": "https://zsolt.blog",

View File

@@ -22,8 +22,8 @@
"clsx": "^1.1.1",
"lz-string": "^1.4.4",
"monkey-around": "^2.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "^5.0.1",
"roughjs": "^4.5.2",
"colormaster": "1.2.1",
@@ -33,7 +33,7 @@
"devDependencies": {
"@babel/core": "^7.16.12",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@babel/preset-react": "^7.18.6",
"@excalidraw/eslint-config": "1.0.0",
"@excalidraw/prettier-config": "1.0.2",
"@popperjs/core": "^2.11.5",
@@ -45,7 +45,7 @@
"@types/js-beautify": "^1.13.3",
"@types/chroma-js": "^2.1.4",
"@types/node": "^15.12.4",
"@types/react-dom": "^17.0.2",
"@types/react-dom": "^18.0.9",
"@zerollup/ts-transform-paths": "^1.7.18",
"cross-env": "^7.0.3",
"eslint-config-prettier": "8.3.0",

View File

@@ -11,6 +11,7 @@ import {
ExcalidrawTextElement,
} from "@zsviczian/excalidraw/types/element/types";
import { normalizePath, Notice, TFile, WorkspaceLeaf } from "obsidian";
import * as obsidian_module from "obsidian";
import ExcalidrawView, { ExportSettings, TextMode } from "./ExcalidrawView";
import { ExcalidrawData, getMarkdownDrawingSection } from "./ExcalidrawData";
import {
@@ -97,6 +98,12 @@ declare global {
}
export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
/**
* Utility function that returns the Obsidian Module object.
*/
get obsidian() {
return obsidian_module;
};
plugin: ExcalidrawPlugin;
targetView: ExcalidrawView = null; //the view currently edited
elementsDict: {[key:string]:any}; //contains the ExcalidrawElements currently edited in Automate indexed by el.id
@@ -398,7 +405,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
const textElements = this.getElements().filter(el => el.type === "text") as ExcalidrawTextElement[];
let outString = "# Text Elements\n";
textElements.forEach(te=> {
outString += `${te.originalText ?? te.text} ^${te.id}\n\n`;
outString += `${te.rawText ?? (te.originalText ?? te.text)} ^${te.id}\n\n`;
});
const elementsWithLinks = this.getElements().filter( el => el.type !== "text" && el.link)
@@ -484,7 +491,8 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
this.getElements(),
this.plugin,
0,
padding
padding,
this.imagesDict
);
};
@@ -538,7 +546,8 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
this.getElements(),
this.plugin,
0,
padding
padding,
this.imagesDict,
);
};
@@ -1251,11 +1260,11 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
errorMessage("targetView not set", "getViewElements()");
return [];
}
const current = this.targetView?.excalidrawRef?.current;
if (!current) {
const api = this.targetView.excalidrawAPI;
if (!api) {
return [];
}
return current?.getSceneElements();
return api.getSceneElements();
};
/**
@@ -2072,6 +2081,14 @@ async function getTemplate(
}
}
if(filenameParts.hasTaskbone) {
groupElements = groupElements.filter( el =>
el.type==="freedraw" ||
( el.type==="image" &&
!plugin.isExcalidrawFile(excalidrawData.getFile(el.fileId)?.file)
));
}
return {
elements: groupElements,
appState: scene.appState,
@@ -2101,6 +2118,7 @@ export async function createPNG(
plugin: ExcalidrawPlugin,
depth: number,
padding?: number,
imagesDict?: any,
) {
if (!loader) {
loader = new EmbeddedFilesLoader(plugin);
@@ -2111,6 +2129,13 @@ export async function createPNG(
: null;
let elements = template?.elements ?? [];
elements = elements.concat(automateElements);
const files = imagesDict ?? {};
if(template?.files) {
Object.values(template.files).forEach((f:any)=>{
files[f.id]=f;
});
}
return await getPNG(
{
type: "excalidraw",
@@ -2122,7 +2147,7 @@ export async function createPNG(
viewBackgroundColor:
template?.appState?.viewBackgroundColor ?? canvasBackgroundColor,
},
files: template?.files ?? {},
files,
},
{
withBackground:
@@ -2146,6 +2171,7 @@ export async function createSVG(
plugin: ExcalidrawPlugin,
depth: number,
padding?: number,
imagesDict?: any,
): Promise<SVGSVGElement> {
if (!loader) {
loader = new EmbeddedFilesLoader(plugin);
@@ -2156,6 +2182,12 @@ export async function createSVG(
let elements = template?.elements ?? [];
elements = elements.concat(automateElements);
padding = padding ?? plugin.settings.exportPaddingSVG;
const files = imagesDict ?? {};
if(template?.files) {
Object.values(template.files).forEach((f:any)=>{
files[f.id]=f;
});
}
const svg = await getSVG(
{
//createAndOpenDrawing
@@ -2168,7 +2200,7 @@ export async function createSVG(
viewBackgroundColor:
template?.appState?.viewBackgroundColor ?? canvasBackgroundColor,
},
files: template?.files ?? {},
files,
},
{
withBackground:

View File

@@ -237,7 +237,7 @@ export class ExcalidrawData {
public elementLinks: Map<string, string> = null;
public scene: any = null;
public deletedElements: ExcalidrawElement[] = [];
private file: TFile = null;
public file: TFile = null;
private app: App;
private showLinkBrackets: boolean;
private linkPrefix: string;
@@ -655,7 +655,7 @@ export class ExcalidrawData {
te,
wrapAt ? wrapText(
originalText,
getFontString(te.fontSize,te.fontFamily),
getFontString({fontSize: te.fontSize, fontFamily: te.fontFamily}),
getMaxContainerWidth(container)
) : originalText,
originalText,
@@ -687,6 +687,7 @@ export class ExcalidrawData {
}
private findNewElementLinksInScene(): boolean {
let result = false;
const elements = this.scene.elements?.filter((el: any) => {
return (
el.type !== "text" &&
@@ -696,25 +697,27 @@ export class ExcalidrawData {
);
});
if (elements.length === 0) {
return false;
return result;
}
let jsonString = JSON.stringify(this.scene);
let id: string; //will be used to hold the new 8 char long ID for textelements that don't yet appear under # Text Elements
for (const el of elements) {
id = el.id;
//replacing Excalidraw element IDs with my own nanoid, because default IDs may contain
//characters not recognized by Obsidian block references
//also Excalidraw IDs are inconveniently long
if (el.id.length > 8) {
result = true;
id = nanoid();
jsonString = jsonString.replaceAll(el.id, id); //brute force approach to replace all occurances (e.g. links, groups,etc.)
}
this.elementLinks.set(id, el.link);
}
this.scene = JSON.parse(jsonString);
return true;
return result;
}
/**

View File

@@ -108,6 +108,9 @@ export interface ExportSettings {
withTheme: boolean;
}
const HIDE = "excalidraw-hidden";
const SHOW = "excalidraw-visible";
export const addFiles = async (
files: FileData[],
view: ExcalidrawView,
@@ -201,6 +204,7 @@ export default class ExcalidrawView extends TextFileView {
public ownerDocument: Document;
public semaphores: {
popoutUnload: boolean; //the unloaded Excalidraw view was the last leaf in the popout window
viewunload: boolean;
//first time initialization of the view
scriptsReady: boolean;
@@ -234,6 +238,7 @@ export default class ExcalidrawView extends TextFileView {
hoverSleep: boolean; //flag with timer to prevent hover preview from being triggered dozens of times
wheelTimeout:NodeJS.Timeout; //used to avoid hover preview while zooming
} = {
popoutUnload: false,
viewunload: false,
scriptsReady: false,
justLoaded: false,
@@ -488,7 +493,7 @@ export default class ExcalidrawView extends TextFileView {
await this.excalidrawData.syncElements(scene);
} else if (
await this.excalidrawData.syncElements(scene, this.excalidrawAPI.getAppState().selectedElementIds)
//&& !this.semaphores.autosaving
&& !this.semaphores.popoutUnload //Obsidian going black after REACT 18 migration when closing last leaf on popout
) {
await this.loadDrawing(
false,
@@ -612,23 +617,6 @@ export default class ExcalidrawView extends TextFileView {
return this.data;
}
addFullscreenchangeEvent() {
//excalidrawWrapperRef.current
this.contentEl.onfullscreenchange = () => {
if (this.plugin.settings.zoomToFitOnResize) {
this.zoomToFit();
}
if (!this.isFullscreen()) {
this.clearFullscreenObserver();
this.contentEl.removeAttribute("style");
}
if (this.toolsPanelRef && this.toolsPanelRef.current) {
this.toolsPanelRef.current.setFullscreen(this.isFullscreen());
}
};
}
fullscreenModalObserver: MutationObserver = null;
private hiddenMobileLeaves:[WorkspaceLeaf,string][] = [];
restoreMobileLeaves() {
@@ -652,99 +640,39 @@ export default class ExcalidrawView extends TextFileView {
if (this.toolsPanelRef && this.toolsPanelRef.current) {
this.toolsPanelRef.current.setFullscreen(true);
}
if (this.plugin.device.isPhone) {
if(Platform.isIosApp) {
this.restoreMobileLeaves();
app.workspace.getLayout().main.children
.filter((child:any)=>child.type==="leaf")
.forEach((listItem:any)=> {
const l = app.workspace.getLeafById(listItem.id);
if(l!==this.leaf) {
//@ts-ignore
this.hiddenMobileLeaves.push([l,l.containerEl.style.display]);
//@ts-ignore
l.containerEl.style.display = "none";
}
});
}
const newStylesheet = document.createElement("style");
newStylesheet.id = "excalidraw-full-screen";
newStylesheet.textContent = `
.workspace-leaf-content .view-content {
padding: 0px !important;
}
.view-header {
height: 1px !important;
}
.status-bar {
display: none !important;
}`;
const oldStylesheet = document.getElementById(newStylesheet.id);
if (oldStylesheet) {
document.head.removeChild(oldStylesheet);
const hide = (el:HTMLElement) => {
while(el && !el.hasClass("workspace-split")) {
el.addClass(SHOW);
el = el.parentElement;
}
document.head.appendChild(newStylesheet);
//return;
if(el) el.addClass(SHOW);
const doc = this.ownerDocument;
doc.body.querySelectorAll(`div.workspace-split:not(.${SHOW})`).forEach(el=>el.addClass(HIDE));
doc.body.querySelector(`div.workspace-leaf-content.${SHOW} > .view-header`).addClass(HIDE);
doc.body.querySelectorAll(`div.workspace-tab-container.${SHOW} > div.workspace-leaf:not(.${SHOW})`).forEach(el=>el.addClass(HIDE));
doc.body.querySelectorAll(`div.workspace-tabs.${SHOW} > div.workspace-tab-header-container`).forEach(el=>el.addClass(HIDE));
doc.body.querySelectorAll(`div.workspace-split.${SHOW} > div.workspace-tabs:not(.${SHOW})`).forEach(el=>el.addClass(HIDE));
doc.body.querySelectorAll(`div.workspace-ribbon`).forEach(el=>el.addClass(HIDE));
doc.body.querySelectorAll(`div.mobile-navbar`).forEach(el=>el.addClass(HIDE));
doc.body.querySelectorAll(`div.status-bar`).forEach(el=>el.addClass(HIDE));
}
this.contentEl.requestFullscreen(); //{navigationUI: "hide"});
this.excalidrawWrapperRef.current.firstElementChild?.focus();
this.contentEl.setAttribute("style", "padding:0px;margin:0px;");
this.fullscreenModalObserver = new MutationObserver((m) => {
if (m.length !== 1) {
return;
}
if (!m[0].addedNodes || m[0].addedNodes.length !== 1) {
return;
}
const node: Node = m[0].addedNodes[0];
if (node.nodeType !== Node.ELEMENT_NODE) {
return;
}
const element = node as HTMLElement;
if (!element.classList.contains("modal-container")) {
return;
}
this.contentEl.appendChild(element);
element.querySelector("input").focus();
});
this.fullscreenModalObserver.observe(this.ownerDocument.body, {
childList: true,
subtree: false,
});
hide(this.contentEl);
}
clearFullscreenObserver() {
if (this.fullscreenModalObserver) {
this.fullscreenModalObserver.disconnect();
this.fullscreenModalObserver = null;
}
}
isFullscreen(): boolean {
return (
this.hiddenMobileLeaves.length > 0 || (
this.ownerDocument.fullscreenEnabled &&
this.ownerDocument.fullscreenElement === this.contentEl) // excalidrawWrapperRef?.current
); //this.contentEl;
return Boolean(document.body.querySelector(".excalidraw-hidden"));
}
exitFullscreen() {
console.log("Exit Fullscreen");
if (this.toolsPanelRef && this.toolsPanelRef.current) {
this.toolsPanelRef.current.setFullscreen(false);
}
if (this.plugin.device.isPhone) {
this.restoreMobileLeaves();
const oldStylesheet = document.getElementById("excalidraw-full-screen");
if (oldStylesheet) {
document.head.removeChild(oldStylesheet);
}
//return;
}
this.ownerDocument.exitFullscreen();
const doc = this.ownerDocument;
doc.querySelectorAll(".excalidraw-hidden").forEach(el=>el.removeClass(HIDE));
doc.querySelectorAll(".excalidraw-visible").forEach(el=>el.removeClass(SHOW));
}
async handleLinkClick(view: ExcalidrawView, ev: MouseEvent) {
@@ -985,6 +913,24 @@ export default class ExcalidrawView extends TextFileView {
wheelEvent: (ev:WheelEvent)=>void;
clearHoverPreview: Function;
public async forceSave(silent:boolean=false) {
if (this.semaphores.autosaving || this.semaphores.saving) {
if(!silent) new Notice("Force Save aborted because saving is in progress)")
return;
}
if(this.preventReloadResetTimer) {
clearTimeout(this.preventReloadResetTimer);
this.preventReloadResetTimer = null;
}
this.semaphores.preventReload = false;
this.semaphores.forceSaving = true;
await this.save(false, true);
this.plugin.triggerEmbedUpdates();
this.loadSceneFiles();
this.semaphores.forceSaving = false;
if(!silent) new Notice("Save successful", 1000);
}
onload() {
const apiMissing = Boolean(typeof this.containerEl.onWindowMigrated === "undefined")
//@ts-ignore
@@ -1015,23 +961,7 @@ export default class ExcalidrawView extends TextFileView {
this.diskIcon = this.addAction(
DISK_ICON_NAME,
t("FORCE_SAVE"),
async () => {
if (this.semaphores.autosaving || this.semaphores.saving) {
new Notice("Force Save aborted because saving is in progress)")
return;
}
if(this.preventReloadResetTimer) {
clearTimeout(this.preventReloadResetTimer);
this.preventReloadResetTimer = null;
}
this.semaphores.preventReload = false;
this.semaphores.forceSaving = true;
await this.save(false, true);
this.plugin.triggerEmbedUpdates();
this.loadSceneFiles();
this.semaphores.forceSaving = false;
new Notice("Save successful", 1000);
},
async () => this.forceSave(),
);
this.textIsRaw_Element = this.addAction(
@@ -1278,6 +1208,7 @@ export default class ExcalidrawView extends TextFileView {
onunload() {
this.restoreMobileLeaves();
this.semaphores.viewunload = true;
this.semaphores.popoutUnload = (this.ownerDocument !== document) && (this.ownerDocument.body.querySelectorAll(".workspace-tab-header").length === 0);
this.ownerWindow?.removeEventListener("keydown", this.onKeyDown, false);
this.ownerWindow?.removeEventListener("keyup", this.onKeyUp, false);
this.containerEl.removeEventListener("wheel", this.wheelEvent, false);
@@ -1301,10 +1232,6 @@ export default class ExcalidrawView extends TextFileView {
clearInterval(this.autosaveTimer);
this.autosaveTimer = null;
}
if (this.fullscreenModalObserver) {
this.fullscreenModalObserver.disconnect();
this.fullscreenModalObserver = null;
}
}
/**
@@ -1443,8 +1370,9 @@ export default class ExcalidrawView extends TextFileView {
public isLoaded: boolean = false;
async setViewData(data: string, clear: boolean = false) {
if(this.plugin.settings.showNewVersionNotification) checkExcalidrawVersion(app);
this.isLoaded = false;
if(!this.file) return;
if(this.plugin.settings.showNewVersionNotification) checkExcalidrawVersion(app);
if (clear) {
this.clear();
}
@@ -1699,7 +1627,7 @@ export default class ExcalidrawView extends TextFileView {
*
* @param justloaded - a flag to trigger zoom to fit after the drawing has been loaded
*/
private async loadDrawing(justloaded: boolean, deletedElements?: ExcalidrawElement[]) {
private async loadDrawing(justloaded: boolean, deletedElements?: ExcalidrawElement[]) {
const excalidrawData = this.excalidrawData.scene;
this.semaphores.justLoaded = justloaded;
this.initialContainerSizeUpdate = justloaded;
@@ -1793,6 +1721,9 @@ export default class ExcalidrawView extends TextFileView {
//console.log(debug);
this.semaphores.dirty = this.file?.path;
this.diskIcon.querySelector("svg").addClass("excalidraw-dirty");
if(!this.semaphores.viewunload && this.toolsPanelRef?.current) {
this.toolsPanelRef.current.setDirty(true);
}
if(!app.isMobile) {
if(requireApiVersion("0.16.0")) {
//@ts-ignore
@@ -1802,11 +1733,15 @@ export default class ExcalidrawView extends TextFileView {
}
public clearDirty() {
if(this.semaphores.viewunload) return;
const api = this.excalidrawAPI;
if (!api) {
return;
}
this.semaphores.dirty = null;
if(this.toolsPanelRef?.current) {
this.toolsPanelRef.current.setDirty(false);
}
const el = api.getSceneElements();
if (el) {
this.previousSceneVersion = this.getSceneVersion(el);
@@ -1821,6 +1756,7 @@ export default class ExcalidrawView extends TextFileView {
}
public initializeToolsIconPanelAfterLoading() {
if(this.semaphores.viewunload) return;
const api = this.excalidrawAPI;
if (!api) {
return;
@@ -2067,7 +2003,6 @@ export default class ExcalidrawView extends TextFileView {
this.loadSceneFiles();
this.updateContainerSize(null, true);
this.excalidrawWrapperRef.current.firstElementChild?.focus();
this.addFullscreenchangeEvent();
this.initializeToolsIconPanelAfterLoading();
},
);
@@ -2290,7 +2225,7 @@ export default class ExcalidrawView extends TextFileView {
ea.style.fontSize = st.currentItemFontSize ?? 20;
ea.style.textAlign = st.currentItemTextAlign ?? "left";
const id = ea.addText(currentPosition.x, currentPosition.y, text);
await this.addElements(ea.getElements(), false, save);
await this.addElements(ea.getElements(), false, save, undefined, true);
return id;
};
@@ -2444,6 +2379,9 @@ export default class ExcalidrawView extends TextFileView {
currentItemStrokeSharpness: st.currentItemStrokeSharpness,
currentItemStartArrowhead: st.currentItemStartArrowhead,
currentItemEndArrowhead: st.currentItemEndArrowhead,
scrollX: st.scrollX,
scrollY: st.scrollY,
zoom: st.zoom,
currentItemLinearStrokeSharpness:
st.currentItemLinearStrokeSharpness,
gridSize: st.gridSize,
@@ -2800,8 +2738,10 @@ export default class ExcalidrawView extends TextFileView {
}
viewModeEnabled = st.viewModeEnabled;
if (this.semaphores.justLoaded) {
const elcount = this.excalidrawData?.scene?.elements?.length ?? 0;
if( elcount>0 && et.length===0 ) return;
this.semaphores.justLoaded = false;
if (!this.semaphores.preventAutozoom) {
if (!this.semaphores.preventAutozoom && this.plugin.settings.zoomToFitOnOpen) {
this.zoomToFit(false,true);
}
this.previousSceneVersion = this.getSceneVersion(et);
@@ -2826,7 +2766,7 @@ export default class ExcalidrawView extends TextFileView {
((sceneVersion > 0 ||
(sceneVersion === 0 && et.length > 0)) && //Addressing the rare case when the last element is deleted from the scene
sceneVersion !== this.previousSceneVersion) ||
st.viewBackgroundColor !== this.previousBackgroundColor
(st.viewBackgroundColor !== this.previousBackgroundColor && this.file === this.excalidrawData.file)
) {
this.previousSceneVersion = sceneVersion;
this.previousBackgroundColor = st.viewBackgroundColor;
@@ -3315,9 +3255,11 @@ export default class ExcalidrawView extends TextFileView {
}
},
onViewModeChange: (isViewModeEnabled: boolean) => {
this.toolsPanelRef?.current?.setExcalidrawViewMode(
isViewModeEnabled,
);
if(!this.semaphores.viewunload) {
this.toolsPanelRef?.current?.setExcalidrawViewMode(
isViewModeEnabled,
);
}
if(this.getHookServer().onViewModeChangeHook) {
try {
this.getHookServer().onViewModeChangeHook(isViewModeEnabled,this,this.getHookServer());
@@ -3362,11 +3304,12 @@ export default class ExcalidrawView extends TextFileView {
return React.createElement(React.Fragment, null, excalidrawDiv);
});
/**REACT 18
//REACT 18
const root = ReactDOM.createRoot(this.contentEl);
root.render(reactElement);
*/
/*REACT 17
ReactDOM.render(reactElement, this.contentEl, () => {});
*/
}
private updateContainerSize(containerId?: string, delay: boolean = false) {

View File

@@ -79,7 +79,7 @@ const getIMG = async (
withTheme: forceTheme ? true : plugin.settings.exportWithTheme,
};
const img = createEl("img");
let style = `max-width:${imgAttributes.fwidth}px !important; width:100%;`;
let style = `max-width:${imgAttributes.fwidth}px; width:100%;`; //removed !important https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/886
if (imgAttributes.fheight) {
style += `height:${imgAttributes.fheight}px;`;
}

View File

@@ -212,8 +212,7 @@ COLOR_NAMES.set("yellow", "#ffff00");
COLOR_NAMES.set("yellowgreen", "#9acd32");
export const DEFAULT_MD_EMBED_CSS = `.snw-reference{display: none;}.excalidraw-md-host{padding:0px 10px}.excalidraw-md-footer{height:5px}foreignObject{background-color:transparent}p{display:block;margin-block-start:1em;margin-block-end:1em;margin-inline-start:0px;margin-inline-end:0px;color:inherit}table,tr,th,td{color:inherit;border:1px solid;border-collapse:collapse;padding:3px}th{font-weight:bold;border-bottom:double;background-color:silver}.copy-code-button{display:none}code[class*=language-],pre[class*=language-]{color:#393a34;font-family:"Consolas","Bitstream Vera Sans Mono","Courier New",Courier,monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;font-size:.9em;line-height:1.2em;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre>code[class*=language-]{font-size:1em}pre[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,code[class*=language-] ::-moz-selection{background:#C1DEF1}pre[class*=language-]::selection,pre[class*=language-] ::selection,code[class*=language-]::selection,code[class*=language-] ::selection{background:#C1DEF1}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;background-color:#0000001a}:not(pre)>code[class*=language-]{padding:.2em;padding-top:1px;padding-bottom:1px;background:#f8f8f8;border:1px solid #dddddd}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:green;font-style:italic}.token.namespace{opacity:.7}.token.string{color:#a31515}.token.punctuation,.token.operator{color:#393a34}.token.url,.token.symbol,.token.number,.token.boolean,.token.variable,.token.constant,.token.inserted{color:#36acaa}.token.atrule,.token.keyword,.token.attr-value,.language-autohotkey .token.selector,.language-json .token.boolean,.language-json .token.number,code[class*=language-css]{color:#00f}.token.function{color:#393a34}.token.deleted,.language-autohotkey .token.tag{color:#9a050f}.token.selector,.language-autohotkey .token.keyword{color:#00009f}.token.important{color:#e90}.token.important,.token.bold{font-weight:bold}.token.italic{font-style:italic}.token.class-name,.language-json .token.property{color:#2b91af}.token.tag,.token.selector{color:maroon}.token.attr-name,.token.property,.token.regex,.token.entity{color:red}.token.directive.tag .tag{background:#ffff00;color:#393a34}.line-numbers.line-numbers .line-numbers-rows{border-right-color:#a5a5a5}.line-numbers .line-numbers-rows>span:before{color:#2b91af}.line-highlight.line-highlight{background:rgba(193,222,241,.2);background:-webkit-linear-gradient(left,rgba(193,222,241,.2) 70%,rgba(221,222,241,0));background:linear-gradient(to right,rgba(193,222,241,.2) 70%,rgba(221,222,241,0))}blockquote{ font-style:italic;background-color:rgb(46,43,42,0.1);margin:0;margin-left:1em;border-radius:0 4px 4px 0;border:1px solid hsl(0,80%,32%);border-left-width:8px;border-top-width:0px;border-right-width:0px;border-bottom-width:0px;padding:10px 20px;margin-inline-start:30px;margin-inline-end:30px;}`;
export const SCRIPTENGINE_ICON = `<g transform="translate(-8,-8)"><path d="M24.318 37.983c-1.234-1.232-8.433-3.903-7.401-7.387 1.057-3.484 9.893-12.443 13.669-13.517 3.776-1.074 6.142 6.523 9.012 7.073 2.87.55 6.797-1.572 8.207-3.694 1.384-2.148-3.147-7.413.15-9.168 3.298-1.755 16.389-2.646 19.611-1.284 3.247 1.363-1.611 7.335-.151 9.483 1.46 2.148 6.067 3.746 8.836 3.38 2.769-.368 4.154-6.733 7.728-5.633 3.575 1.1 12.36 8.828 13.67 12.233 1.308 3.406-5.186 5.423-5.79 8.2-.58 2.75-.026 6.705 2.265 8.355 2.266 1.65 9.642-1.78 11.404 1.598 1.762 3.38 1.007 15.35-.806 18.651-1.787 3.353-7.753-.367-9.969 1.31-2.215 1.65-3.901 5.92-3.373 8.67.504 2.777 7.754 4.48 6.445 7.885C96.49 87.543 87.15 95.454 83.5 96.685c-3.65 1.231-4.96-4.741-7.577-5.16-2.593-.393-6.57.707-8.03 2.75-1.436 2.017 2.668 7.806-.63 9.483-3.323 1.676-15.759 2.226-19.157.655-3.373-1.598.554-7.964-1.108-10.138-1.687-2.174-6.394-3.431-9.012-2.907-2.643.55-3.273 7.282-6.747 6.103-3.499-1.126-12.788-9.535-14.172-13.019-1.36-3.484 5.437-5.108 5.966-7.858.529-2.777-.68-7.073-2.744-8.697-2.064-1.624-7.93 2.41-9.642-1.126-1.737-3.537-2.441-16.765-.654-20.118 1.787-3.3 9.062 1.598 11.429.183 2.366-1.44 2.316-7.282 2.769-8.749m.126-.104c-1.234-1.232-8.433-3.903-7.401-7.387 1.057-3.484 9.893-12.443 13.669-13.517 3.776-1.074 6.142 6.523 9.012 7.073 2.87.55 6.797-1.572 8.207-3.694 1.384-2.148-3.147-7.413.15-9.168 3.298-1.755 16.389-2.646 19.611-1.284 3.247 1.363-1.611 7.335-.151 9.483 1.46 2.148 6.067 3.746 8.836 3.38 2.769-.368 4.154-6.733 7.728-5.633 3.575 1.1 12.36 8.828 13.67 12.233 1.308 3.406-5.186 5.423-5.79 8.2-.58 2.75-.026 6.705 2.265 8.355 2.266 1.65 9.642-1.78 11.404 1.598 1.762 3.38 1.007 15.35-.806 18.651-1.787 3.353-7.753-.367-9.969 1.31-2.215 1.65-3.901 5.92-3.373 8.67.504 2.777 7.754 4.48 6.445 7.885C96.49 87.543 87.15 95.454 83.5 96.685c-3.65 1.231-4.96-4.741-7.577-5.16-2.593-.393-6.57.707-8.03 2.75-1.436 2.017 2.668 7.806-.63 9.483-3.323 1.676-15.759 2.226-19.157.655-3.373-1.598.554-7.964-1.108-10.138-1.687-2.174-6.394-3.431-9.012-2.907-2.643.55-3.273 7.282-6.747 6.103-3.499-1.126-12.788-9.535-14.172-13.019-1.36-3.484 5.437-5.108 5.966-7.858.529-2.777-.68-7.073-2.744-8.697-2.064-1.624-7.93 2.41-9.642-1.126-1.737-3.537-2.441-16.765-.654-20.118 1.787-3.3 9.062 1.598 11.429.183 2.366-1.44 2.316-7.282 2.769-8.749" fill="none" stroke-width="2" stroke-linecap="round" stroke="currentColor"/><path d="M81.235 56.502a23.3 23.3 0 0 1-1.46 8.068 20.785 20.785 0 0 1-1.762 3.72 24.068 24.068 0 0 1-5.337 6.26 22.575 22.575 0 0 1-3.449 2.358 23.726 23.726 0 0 1-7.803 2.803 24.719 24.719 0 0 1-8.333 0 24.102 24.102 0 0 1-4.028-1.074 23.71 23.71 0 0 1-3.776-1.729 23.259 23.259 0 0 1-6.369-5.265 23.775 23.775 0 0 1-2.416-3.353 24.935 24.935 0 0 1-1.762-3.72 23.765 23.765 0 0 1-1.083-3.981 23.454 23.454 0 0 1 0-8.173c.252-1.336.604-2.698 1.083-3.956a24.935 24.935 0 0 1 1.762-3.72 22.587 22.587 0 0 1 2.416-3.378c.881-1.048 1.888-2.017 2.946-2.908a24.38 24.38 0 0 1 3.423-2.357 23.71 23.71 0 0 1 3.776-1.73 21.74 21.74 0 0 1 4.028-1.047 23.437 23.437 0 0 1 8.333 0 24.282 24.282 0 0 1 7.803 2.777 26.198 26.198 0 0 1 3.45 2.357 24.62 24.62 0 0 1 5.336 6.287 20.785 20.785 0 0 1 1.762 3.72 21.32 21.32 0 0 1 1.083 3.955c.251 1.336.302 3.405.377 4.086.05.681.05-.68 0 0" fill="none" stroke-width="4" stroke-linecap="round" stroke="currentColor"/><path d="M69.404 56.633c-6.596-3.3-13.216-6.6-19.51-9.744m19.51 9.744c-6.747-3.379-13.493-6.758-19.51-9.744m0 0v19.489m0-19.49v19.49m0 0c4.355-2.148 8.71-4.322 19.51-9.745m-19.51 9.745c3.978-1.965 7.93-3.956 19.51-9.745m0 0h0m0 0h0" fill="currentColor" stroke-linecap="round" stroke="currentColor" stroke-width="4"/></g>`;
export const DISK_ICON_NAME = "disk";
export const DISK_ICON = `<path fill="none" stroke="currentColor" fill="#fff" d="M0 0h100v100H0z"/><path fill="none" stroke="currentColor" d="M20.832 4.168c21.824.145 43.645.289 74.68.5m-74.68-.5c17.09.113 34.176.227 74.68.5m0 0c.094 27.3.191 54.602.32 91.164m-.32-91.164c.113 32.633.23 65.27.32 91.164m0 0H4.168m91.664 0H4.168m0 0v-75m0 75v-75m0 0L20.832 4.168M4.168 20.832L20.832 4.168M20.832 4.168h58.336m-58.336 0h58.336m0 0v25m0-25v25m0 0H20.832m58.336 0H20.832m0 0v-25m0 25v-25" stroke-width="1.66668" /><path fill="none" stroke="currentColor" d="M29.168 4.168h16.664v16.664H29.168"/><path fill="none" stroke="currentColor" d="M29.168 4.168h16.664m-16.664 0h16.664m0 0v16.664m0-16.664v16.664m0 0H29.168m16.664 0H29.168m0 0V4.168m0 16.664V4.168M12.5 54.168h75m-75 0h75m0 0v41.664m0-41.664v41.664m0 0h-75m75 0h-75m0 0V54.168m0 41.664V54.168M20.832 62.5c20.11-.18 40.219-.36 55.68-.5m-55.68.5c14.656-.133 29.313-.262 55.68-.5M20.832 71.332c13.098-.117 26.2-.234 55.68-.5m-55.68.5l55.68-.5M21.117 79.582c20.645-.184 41.285-.371 55.68-.5m-55.68.5c18.153-.16 36.301-.324 55.68-.5" stroke-width="1.66668"/>`;
export const DISK_ICON_NAME = "save";
export const PNG_ICON_NAME = "save-png";
export const PNG_ICON = `<defs><symbol overflow="visible" id="aa"><path fill="currentColor" stroke="currentColor" d="M6.578-10.984h8.188c2.03 0 3.64-.594 5.046-1.844 1.563-1.422 2.25-3.094 2.25-5.469 0-4.875-2.906-7.61-8.046-7.61H3.25V0h3.328zm0-2.907v-9.093h6.938c3.171 0 5.078 1.703 5.078 4.547 0 2.843-1.907 4.546-5.078 4.546zm0 0"></path></symbol><symbol overflow="visible" id="bb"><path fill="currentColor" stroke="currentColor" d="M23.094-25.906h-3.14V-4.72L6.327-25.906h-3.61V0H5.86v-21L19.344 0h3.75zm0 0"></path></symbol><symbol overflow="visible" id="cc"><path fill="currentColor" stroke="currentColor" d="M25.344-13.672h-10.86v2.906h7.938v.704c0 4.624-3.438 7.968-8.188 7.968-2.656 0-5.046-.969-6.578-2.625-1.718-1.86-2.765-4.953-2.765-8.14 0-6.36 3.656-10.563 9.156-10.563 3.969 0 6.828 2.031 7.547 5.375h3.39c-.922-5.265-4.922-8.281-10.906-8.281-3.172 0-5.75.812-7.781 2.484-3.047 2.485-4.719 6.5-4.719 11.157 0 7.968 4.89 13.5 11.938 13.5 3.53 0 6.328-1.313 8.906-4.11l.812 3.438h2.11zm0 0"></path></symbol></defs><path fill="none" stroke="currentColor" d="M-.003.003v59.999m0-60v60m0 0h220.006m-220.006 0h220.006m0 0v-60m0 60v-60" transform="matrix(.40833 0 0 .40574 4.083 68.975)" stroke-width="4"></path><use xlink:href="#aa" x="11.023" y="86.651"></use><use xlink:href="#bb" x="33.944" y="86.651"></use><use xlink:href="#cc" x="59.724" y="86.651"></use><path fill="currentColor" stroke="currentColor" d="M40.832 4.059h16.336v32.457h8.164L49 52.746l-16.332-16.23h8.164V4.059" fill-rule="evenodd"></path><path fill="currentColor" stroke="currentColor" d="M-.003.003h40.006m-40.006 0h40.006m0 0v79.995m0-79.995v79.995m0 0h19.994m-19.994 0h19.994m0 0C51.55 88.451 43.093 96.904 20 120m39.997-40.002A196001.962 196001.962 0 0120 120m0 0C8.406 108.41-3.18 96.817-19.997 79.998M20 120C9.43 109.43-1.142 98.858-19.997 79.998m0 0H-.003m-19.994 0H-.003m0 0V.003m0 79.995V.003m0 0h0m0 0h0" transform="matrix(.40833 0 0 .40574 40.833 4.057)" stroke-width="4"></path>`;
export const SVG_ICON_NAME = "save-svg";

View File

@@ -17,6 +17,54 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
<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.8.1": `
## New and fixes from Excalidraw.com
- New text paste behavior. Pasting multiline text will generate separate text elements unless you hold down the shift button while pasting [#5786](https://github.com/excalidraw/excalidraw/pull/5786)
- line editor fixes [#5927](https://github.com/excalidraw/excalidraw/pull/5927)
## Fixed
- The Command Palette "Insert link" action now inserts the new link at the top drawing layer, not at the bottom.
- Updated, hopefully, better organized, Plugin Readme.
## New
- Second attempt at moving to React 18. This upgrade is required to maintain alignment with the core Excalidraw product and to continue to benefit from Excalidraw.com enhancements.
- Added options to Plugin Settings
- to disable autozoom when loading a drawing for the first time [#907](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/907)
- to modify autosave interval. You can now set an autosave interval for desktop and for mobile [#888](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/888)
## New in ExcalidrawAutomate
- Published the obsidian_module on the ExcalidrawAutomate object. ${String.fromCharCode(96)}ExcalidrawAutomate.obsidian${String.fromCharCode(96)}. Publishing this object will give script developers increased flexibility and control over script automation.
`,
"1.8.0": `
<div class="excalidraw-videoWrapper"><div>
<iframe src="https://www.youtube.com/embed/7gu4ETx7zro" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</div></div>
## New
- Optical Character Recognition (OCR). Introducing the MVP (minimum viable product) release of the integration of [Taskbone](https://taskbone.com) OCR into Excalidraw. See the new scan button on the Obsidian tools panel.
- New and improved full-screen mode
- Activate using the Obsidian tools panel, the Obsidian Command Palette, or the Alt+F11 shortcut
- The ESC key no longer closes full-screen
- Full-screen mode works properly on iOS as well
- Improved Icon visibility on the Obsidian tools panel
- Added 3 additional buttons to the tools panel
- Force save
- Open link (useful on Mobile devices). In the case of LaTeX equations, the button opens the equation properties.
- Open the link in a new pane. In the case of embedded markdown documents, the button opens the embed properties.
## Fixed
- The [deconstruct selected elements into a new drawing](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Deconstruct%20selected%20elements%20into%20new%20drawing.md) script now also correctly decomposes transcluded text elements.
`,
"1.7.30":`
Fix:
- Forcing the embedded image to always scale to 100% (a feature introduced in [1.7.26](https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/1.7.26)) scaled the embedded excalidraw drawings incorrectly on devices with a pixel ratio of 2 or 3 (e.g. iPads). This is now fixed, however, this fix might retrospectively impact drawings that use this feature. Sorry for that.
`,
"1.7.29":`
- This is a big update that accommodates the **UI redesign** on Excalidraw.com [#5780](https://github.com/excalidraw/excalidraw/pull/5780). The change on the surface may seem superficial, however, I had to tweak a number of things to make it work in Obsidian. I hope I found everything that broke and fixed it, if not, I'll try to fix it quickly...
- This update also comes with changes under the hood that **fix issues with Excalidraw Automate** - paving the way for further scripts, plus some smaller bug fixes.
- I **reworked text wrapping**. In some cases, text wrapping in SVG exports looked different compared to how the text looked in Excalidraw. This should now be fixed.
- If you are using the **Experimental Dynamic Styling** of the Excalidraw Toolbar, then I recommend updating your styling script following base on [this](https://gist.github.com/zsviczian/c7223c5b4af30d5c88a0cae05300305c)
`,
"1.7.27":`## New
- Import SVG drawing as an Excalidraw object. [#679](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/679)

View File

@@ -486,6 +486,12 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
desc: "Converts a CSS color name to its HEX color equivalent. 'White' to #FFFFFF",
after: "",
},
{
field: "obsidian",
code: "obsidian",
desc: "Access functions and objects available on the <a onclick='window.open(\"https://github.com/obsidianmd/obsidian-api/blob/master/obsidian.d.ts\")'>Obsidian Module</a>",
after: "",
}
];
export const EXCALIDRAW_SCRIPTENGINE_INFO: SuggesterInfo[] = [

View File

@@ -59,6 +59,7 @@ export default {
"Insert LaTeX formula (e.g. \\binom{n}{k} = \\frac{n!}{k!(n-k)!})",
ENTER_LATEX: "Enter a valid LaTeX expression",
READ_RELEASE_NOTES: "Read latest release notes",
RUN_OCR: "OCR: Grab text from freedraw scribble and pictures to clipboard",
TRAY_MODE: "Toggle property-panel tray-mode",
SEARCH: "Search for text in drawing",
RESET_IMG_TO_100: "Set selected image element size to 100% of original",
@@ -122,6 +123,7 @@ export default {
"You can access your scripts from Excalidraw via the Obsidian Command Palette. Assign " +
"hotkeys to your favorite scripts just like to any other Obsidian command. " +
"The folder may not be the root folder of your Vault. ",
SAVING_HEAD: "Saving",
COMPRESS_NAME: "Compress Excalidraw JSON in Markdown",
COMPRESS_DESC:
"By enabling this feature Excalidraw will store the drawing JSON in a Base64 compressed " +
@@ -133,15 +135,18 @@ export default {
"once you switch back to Excalidraw view. " +
"The setting only has effect 'point forward', meaning, existing drawings will not be effected by the setting " +
"until you open them and save them.<br><b>Toggle ON:</b> Compress drawing JSON<br><b>Toggle OFF:</b> Leave drawing JSON uncompressed",
AUTOSAVE_NAME: "Enable Autosave",
AUTOSAVE_DESC:
"Automatically save the active drawing, in case there are changes, every 15, 30 seconds, or 1, 2, 3, 4, or 5 minute. Save normally happens when you close Excalidraw or Obsidian, or move " +
"focus to another pane. I created this feature with mobile " +
"phones and tablets in mind, where 'swiping out Obsidian to close it' led to some data loss.",
AUTOSAVE_INTERVAL_NAME: "Interval for autosave",
AUTOSAVE_INTERVAL_DESC:
"The time interval between saves. Autosave will skip if there are no changes in the drawing.",
FILENAME_HEAD: "Filename",
AUTOSAVE_INTERVAL_DESKTOP_NAME: "Interval for autosave on Desktop",
AUTOSAVE_INTERVAL_DESKTOP_DESC:
"The time interval between saves. Autosave will skip if there are no changes in the drawing. " +
"Excalidraw will also save the file when closing a workspace tab or navigating within Obsidian, but away from the active Excalidraw tab (i.e. clicking on the Obsidian ribbon or checking backlinks, etc.). " +
"Excalidraw will not be able to save your work when terminating Obsidian directly either by killing the Obsidian process, or clicking to close Obsidian altogether.",
AUTOSAVE_INTERVAL_MOBILE_NAME: "Interval for autosave on Mobile",
AUTOSAVE_INTERVAL_MOBILE_DESC:
"I recommend a more frequent interval for Mobiles. " +
"Excalidraw will also save the file when closing a workspace tab or navigating within Obsidian, but away from the active Excalidraw tab (i.e. tapping on the Obsidian ribbon or checking backlinks, etc.). " +
"Excalidraw will not be able to save your work when terminating Obsidian directly (i.e. swiping it away). Also note, that when you switch apps on a Mobile device, sometimes Android and iOS closes " +
"Obsidian in the background to save system resources. In such a case Excalidraw will not be able to save the latest changes.",
FILENAME_HEAD: "Filename",
FILENAME_DESC:
"<p>Click this link for the <a href='https://momentjs.com/docs/#/displaying/format/'>" +
"date and time format reference</a>.</p>",
@@ -194,6 +199,9 @@ export default {
ZOOM_TO_FIT_NAME: "Zoom to fit on view resize",
ZOOM_TO_FIT_DESC: "Zoom to fit drawing when the pane is resized" +
"<br><b>Toggle ON:</b> Zoom to fit<br><b>Toggle OFF:</b> Auto zoom disabled",
ZOOM_TO_FIT_ONOPEN_NAME: "Zoom to fit on file open",
ZOOM_TO_FIT_ONOPEN_DESC: "Zoom to fit drawing when the drawing is first opened" +
"<br><b>Toggle ON:</b> Zoom to fit<br><b>Toggle OFF:</b> Auto zoom disabled",
ZOOM_TO_FIT_MAX_LEVEL_NAME: "Zoom to fit max ZOOM level",
ZOOM_TO_FIT_MAX_LEVEL_DESC:
"Set the maximum level to which zoom to fit will enlarge the drawing. Minimum is 0.5 (50%) and maximum is 10 (1000%).",
@@ -418,6 +426,18 @@ export default {
"Select a .ttf, .woff or .woff2 font file from your vault to use as the fourth font. " +
"If no file is selected, Excalidraw will use the Virgil font by default.",
SCRIPT_SETTINGS_HEAD: "Settings for installed Scripts",
TASKBONE_HEAD: "Taskbone Optical Character Recogntion",
TASKBONE_DESC: "This is an experimental integration of optical character recognition into Excalidraw. Please note, that taskbone is an independent external service not provided by Excalidraw, nor the Excalidraw-Obsidian plugin project. " +
"The OCR service will grab legible text from freedraw lines and embedded pictures on your canvas and place the recognized text in the frontmatter of your drawing as well as onto clipboard. " +
"Having the text in the frontmatter will enable you to search in Obsidian for the text contents of these. " +
"Note, that the process of extracting the text from the image is not done locally, but via an online API. The taskbone service stores the image on its servers only as long as necessary for the text extraction. However, if this is a dealbreaker, then please don't use this feature.",
TASKBONE_ENABLE_NAME: "Enable Taskbone",
TASKBONE_ENABLE_DESC: "By enabling this service your agree to the Taskbone <a href='https://www.taskbone.com/legal/terms/' target='_blank'>Terms and Conditaions</a> and the " +
"<a href='https://www.taskbone.com/legal/privacy/' target='_blank'>Privacy Policy</a>.",
TASKBONE_APIKEY_NAME: "Taskbone API Key",
TASKBONE_APIKEY_DESC: "Taskbone offers a free service with a reasonable number of scans per month. If you want to use this feature more frequently, or you want to supoprt " +
"the developer of Taskbone (as you can imagine, there is no such thing as 'free', providing this awesome OCR service costs some money to the developer of Taskbone), you can " +
"purchase a paid API key from <a href='https://www.taskbone.com/' target='_blank'>taskbone.com</a>. In case you have purchased a key, simply overwrite this auto generated free-tier API-key with your paid key.",
//openDrawings.ts
SELECT_FILE: "Select a file then press enter.",
@@ -446,4 +466,6 @@ export default {
GOTO_FULLSCREEN: "Goto fullscreen mode",
EXIT_FULLSCREEN: "Exit fullscreen mode",
TOGGLE_FULLSCREEN: "Toggle fullscreen mode",
OPEN_LINK_CLICK: "Navigate to selected element link",
OPEN_LINK_PROPS: "Open markdown-embed properties or open link in new window"
};

View File

@@ -25,8 +25,6 @@ import {
ICON_NAME,
SCRIPTENGINE_ICON,
SCRIPTENGINE_ICON_NAME,
DISK_ICON,
DISK_ICON_NAME,
PNG_ICON,
PNG_ICON_NAME,
SVG_ICON,
@@ -105,6 +103,7 @@ import { Packages } from "./types";
import * as React from "react";
import { ScriptInstallPrompt } from "./dialogs/ScriptInstallPrompt";
import { check } from "prettier";
import Taskbone from "./ocr/Taskbone";
declare module "obsidian" {
@@ -133,6 +132,7 @@ declare const excalidrawLib: any;
declare const PLUGIN_VERSION:string;
export default class ExcalidrawPlugin extends Plugin {
public taskbone: Taskbone;
private excalidrawFiles: Set<TFile> = new Set<TFile>();
public excalidrawFileModes: { [file: string]: string } = {};
private _loaded: boolean = false;
@@ -222,7 +222,6 @@ export default class ExcalidrawPlugin extends Plugin {
addIcon(ICON_NAME, EXCALIDRAW_ICON);
addIcon(SCRIPTENGINE_ICON_NAME, SCRIPTENGINE_ICON);
addIcon(DISK_ICON_NAME, DISK_ICON);
addIcon(PNG_ICON_NAME, PNG_ICON);
addIcon(SVG_ICON_NAME, SVG_ICON);
@@ -255,8 +254,8 @@ export default class ExcalidrawPlugin extends Plugin {
// 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 obsidianJustInstalled = this.settings.previousRelease === "0.0.0"
if (isVersionNewerThanOther(PLUGIN_VERSION, this.settings.previousRelease)) {
new ReleaseNotes(
this.app,
@@ -274,6 +273,7 @@ export default class ExcalidrawPlugin extends Plugin {
this.app.workspace.onLayoutReady(() => {
this.scriptEngine = new ScriptEngine(self);
});
this.taskbone = new Taskbone(this);
}
public initializeFourthFont() {
@@ -395,23 +395,12 @@ export default class ExcalidrawPlugin extends Plugin {
}
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
if (view) {
(async()=>{
if (view.semaphores.autosaving) {
return;
}
view.semaphores.forceSaving = true;
await view.save(false, true);
view.plugin.triggerEmbedUpdates();
view.loadSceneFiles();
view.semaphores.forceSaving = false;
new Notice("Save successful", 1000);
})();
view.forceSave();
return true;
}
return false;
}
private registerInstallCodeblockProcessor() {
const codeblockProcessor = async (source: string, el: HTMLElement) => {
//Button next to the "List of available scripts" at the top
@@ -966,6 +955,28 @@ export default class ExcalidrawPlugin extends Plugin {
},
});
this.addCommand({
id: "run-ocr",
name: t("RUN_OCR"),
checkCallback: (checking: boolean) => {
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
if (checking) {
return (
Boolean(view)
);
}
if (view) {
if(!this.settings.taskboneEnabled) {
new Notice("Taskbone OCR is not enabled. Please go to plugins settings to enable it.",4000);
return true;
}
this.taskbone.getTextForView(view, false);
return true;
}
return false;
},
});
this.addCommand({
id: "search-text",
name: t("SEARCH"),
@@ -1734,7 +1745,7 @@ 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
if(previouslyActiveEV.semaphores.dirty) {
if(previouslyActiveEV.semaphores.dirty && !previouslyActiveEV.semaphores.viewunload) {
await previouslyActiveEV.save(true); //this will update transclusions in the drawing
}
}
@@ -2053,7 +2064,9 @@ export default class ExcalidrawPlugin extends Plugin {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
if(opts.applyLefthandedMode) setLeftHandedMode(this.settings.isLeftHanded);
if(opts.reEnableAutosave) this.settings.autosave = true;
this.settings.autosaveInterval = app.isMobile?10000:15000; //more frequent on mobile because Obsidian may be killed on context switching
this.settings.autosaveInterval = app.isMobile
? this.settings.autosaveIntervalMobile
: this.settings.autosaveIntervalDesktop;
}
async saveSettings() {
@@ -2259,7 +2272,8 @@ export default class ExcalidrawPlugin extends Plugin {
}
public isExcalidrawFile(f: TFile) {
if (f.extension == "excalidraw") {
if(!f) return false;
if (f.extension === "excalidraw") {
return true;
}
const fileCache = f ? this.app.metadataCache.getFileCache(f) : null;

View File

@@ -181,6 +181,70 @@ export const ICONS = {
<path d="M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z" />
</svg>
),
ocr: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="var(--icon-fill-color)"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="m6 18 6-12 6 12"></path><path d="M8 14h8"></path><path d="M3 7V5a2 2 0 0 1 2-2h2"></path><path d="M17 3h2a2 2 0 0 1 2 2v2"></path><path d="M21 17v2a2 2 0 0 1-2 2h-2"></path><path d="M7 21H5a2 2 0 0 1-2-2v-2"></path>
</svg>
),
scriptEngine: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
stroke="var(--icon-fill-color)"
fill="var(--icon-fill-color)"
strokeLinecap="round"
strokeWidth="4"
>
<g transform="translate(-8,-8)">
<path d="M24.318 37.983c-1.234-1.232-8.433-3.903-7.401-7.387 1.057-3.484 9.893-12.443 13.669-13.517 3.776-1.074 6.142 6.523 9.012 7.073 2.87.55 6.797-1.572 8.207-3.694 1.384-2.148-3.147-7.413.15-9.168 3.298-1.755 16.389-2.646 19.611-1.284 3.247 1.363-1.611 7.335-.151 9.483 1.46 2.148 6.067 3.746 8.836 3.38 2.769-.368 4.154-6.733 7.728-5.633 3.575 1.1 12.36 8.828 13.67 12.233 1.308 3.406-5.186 5.423-5.79 8.2-.58 2.75-.026 6.705 2.265 8.355 2.266 1.65 9.642-1.78 11.404 1.598 1.762 3.38 1.007 15.35-.806 18.651-1.787 3.353-7.753-.367-9.969 1.31-2.215 1.65-3.901 5.92-3.373 8.67.504 2.777 7.754 4.48 6.445 7.885C96.49 87.543 87.15 95.454 83.5 96.685c-3.65 1.231-4.96-4.741-7.577-5.16-2.593-.393-6.57.707-8.03 2.75-1.436 2.017 2.668 7.806-.63 9.483-3.323 1.676-15.759 2.226-19.157.655-3.373-1.598.554-7.964-1.108-10.138-1.687-2.174-6.394-3.431-9.012-2.907-2.643.55-3.273 7.282-6.747 6.103-3.499-1.126-12.788-9.535-14.172-13.019-1.36-3.484 5.437-5.108 5.966-7.858.529-2.777-.68-7.073-2.744-8.697-2.064-1.624-7.93 2.41-9.642-1.126-1.737-3.537-2.441-16.765-.654-20.118 1.787-3.3 9.062 1.598 11.429.183 2.366-1.44 2.316-7.282 2.769-8.749m.126-.104c-1.234-1.232-8.433-3.903-7.401-7.387 1.057-3.484 9.893-12.443 13.669-13.517 3.776-1.074 6.142 6.523 9.012 7.073 2.87.55 6.797-1.572 8.207-3.694 1.384-2.148-3.147-7.413.15-9.168 3.298-1.755 16.389-2.646 19.611-1.284 3.247 1.363-1.611 7.335-.151 9.483 1.46 2.148 6.067 3.746 8.836 3.38 2.769-.368 4.154-6.733 7.728-5.633 3.575 1.1 12.36 8.828 13.67 12.233 1.308 3.406-5.186 5.423-5.79 8.2-.58 2.75-.026 6.705 2.265 8.355 2.266 1.65 9.642-1.78 11.404 1.598 1.762 3.38 1.007 15.35-.806 18.651-1.787 3.353-7.753-.367-9.969 1.31-2.215 1.65-3.901 5.92-3.373 8.67.504 2.777 7.754 4.48 6.445 7.885C96.49 87.543 87.15 95.454 83.5 96.685c-3.65 1.231-4.96-4.741-7.577-5.16-2.593-.393-6.57.707-8.03 2.75-1.436 2.017 2.668 7.806-.63 9.483-3.323 1.676-15.759 2.226-19.157.655-3.373-1.598.554-7.964-1.108-10.138-1.687-2.174-6.394-3.431-9.012-2.907-2.643.55-3.273 7.282-6.747 6.103-3.499-1.126-12.788-9.535-14.172-13.019-1.36-3.484 5.437-5.108 5.966-7.858.529-2.777-.68-7.073-2.744-8.697-2.064-1.624-7.93 2.41-9.642-1.126-1.737-3.537-2.441-16.765-.654-20.118 1.787-3.3 9.062 1.598 11.429.183 2.366-1.44 2.316-7.282 2.769-8.749" fill="none" strokeWidth="2"/>
<path d="M81.235 56.502a23.3 23.3 0 0 1-1.46 8.068 20.785 20.785 0 0 1-1.762 3.72 24.068 24.068 0 0 1-5.337 6.26 22.575 22.575 0 0 1-3.449 2.358 23.726 23.726 0 0 1-7.803 2.803 24.719 24.719 0 0 1-8.333 0 24.102 24.102 0 0 1-4.028-1.074 23.71 23.71 0 0 1-3.776-1.729 23.259 23.259 0 0 1-6.369-5.265 23.775 23.775 0 0 1-2.416-3.353 24.935 24.935 0 0 1-1.762-3.72 23.765 23.765 0 0 1-1.083-3.981 23.454 23.454 0 0 1 0-8.173c.252-1.336.604-2.698 1.083-3.956a24.935 24.935 0 0 1 1.762-3.72 22.587 22.587 0 0 1 2.416-3.378c.881-1.048 1.888-2.017 2.946-2.908a24.38 24.38 0 0 1 3.423-2.357 23.71 23.71 0 0 1 3.776-1.73 21.74 21.74 0 0 1 4.028-1.047 23.437 23.437 0 0 1 8.333 0 24.282 24.282 0 0 1 7.803 2.777 26.198 26.198 0 0 1 3.45 2.357 24.62 24.62 0 0 1 5.336 6.287 20.785 20.785 0 0 1 1.762 3.72 21.32 21.32 0 0 1 1.083 3.955c.251 1.336.302 3.405.377 4.086.05.681.05-.68 0 0" fill="none"/>
<path d="M69.404 56.633c-6.596-3.3-13.216-6.6-19.51-9.744m19.51 9.744c-6.747-3.379-13.493-6.758-19.51-9.744m0 0v19.489m0-19.49v19.49m0 0c4.355-2.148 8.71-4.322 19.51-9.745m-19.51 9.745c3.978-1.965 7.93-3.956 19.51-9.745m0 0h0m0 0h0"/>
</g>
</svg>
),
openLink: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="var(--icon-fill-color)"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M4 22h14a2 2 0 0 0 2-2V7.5L14.5 2H6a2 2 0 0 0-2 2v7"/>
<polyline points="14 2 14 8 20 8"/>
<path d="m10 18 3-3-3-3"/>
<path d="M4 18v-1a2 2 0 0 1 2-2h6"/>
</svg>
),
openLinkProperties: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="var(--icon-fill-color)"
strokeWidth="3"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M4 22h14a2 2 0 0 0 2-2V7.5L14.5 2H6a2 2 0 0 0-2 2v7"/>
<polyline
points="14 2 14 8 20 8"
fill="var(--icon-fill-color)"
/>
<path d="m10 18 3-3-3-3"/>
<path d="M4 18v-1a2 2 0 0 1 2-2h6"/>
</svg>
),
//fa-brands fa-markdown
switchToMarkdown: (
<svg
@@ -380,12 +444,30 @@ export const ICONS = {
)
};
export const saveIcon = (isDirty: boolean) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke={isDirty?"var(--color-accent)":"var(--icon-fill-color)"}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
<polyline points="17 21 17 13 7 13 7 21"/>
<polyline points="7 3 7 8 15 8"/>
</svg>
)
}
export const stringToSVG = (svg: string) => {
svg = svg
.replace(/stroke\s*=\s*['"][^"']*['"]/g,"")
.replace(/width\s*=\s*['"][^"']*['"]/g,"")
.replace(/height\s*=\s*['"][^"']*['"]/g,"")
.replace("<svg ",`<svg style="stroke:var(--icon-fill-color);color:var(--icon-fill-color);fill:var(--icon-fill-color)" `)
.replace("<svg ",`<svg style="stroke:var(--icon-fill-color);color:var(--icon-fill-color);fill:var(--icon-fill-color);stroke-width:6;" `)
return (
<div dangerouslySetInnerHTML={{__html: svg}}></div>

View File

@@ -2,7 +2,7 @@ import clsx from "clsx";
import { Notice, TFile } from "obsidian";
import * as React from "react";
import { ActionButton } from "./ActionButton";
import { ICONS, stringToSVG } from "./ActionIcons";
import { ICONS, saveIcon, stringToSVG } from "./ActionIcons";
import { SCRIPT_INSTALL_FOLDER, CTRL_OR_CMD } from "../Constants";
import { insertLaTeXToView, search } from "../ExcalidrawAutomate";
import ExcalidrawView, { TextMode } from "../ExcalidrawView";
@@ -10,6 +10,7 @@ import { t } from "../lang/helpers";
import { ReleaseNotes } from "../dialogs/ReleaseNotes";
import { ScriptIconMap } from "../Scripts";
import { getIMGFilename } from "../utils/FileUtils";
import { ScriptInstallPrompt } from "src/dialogs/ScriptInstallPrompt";
declare const PLUGIN_VERSION:string;
const dark = '<svg style="stroke:#ced4da;#212529;color:#ced4da;fill:#ced4da" ';
@@ -28,6 +29,7 @@ export type PanelState = {
theme: "dark" | "light";
excalidrawViewMode: boolean;
minimized: boolean;
isDirty: boolean;
isFullscreen: boolean;
isPreviewMode: boolean;
scriptIconMap: ScriptIconMap;
@@ -59,6 +61,7 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
theme: "dark",
excalidrawViewMode: false,
minimized: false,
isDirty: false,
isFullscreen: false,
isPreviewMode: true,
scriptIconMap: {},
@@ -87,6 +90,14 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
});
}
setDirty(isDirty: boolean) {
this.setState(()=> {
return {
isDirty,
};
});
}
setExcalidrawViewMode(isViewModeEnabled: boolean) {
this.setState(() => {
return {
@@ -267,13 +278,13 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
<fieldset>
<legend>Utility actions</legend>
<div className="buttonList buttonListIcon">
<ActionButton
key={"search"}
title={t("SEARCH")}
<ActionButton
key={"scriptEngine"}
title={t("INSTALL_SCRIPT_BUTTON")}
action={() => {
search(this.props.view);
new ScriptInstallPrompt(this.props.view.plugin).open();
}}
icon={ICONS.search}
icon={ICONS.scriptEngine}
view={this.props.view}
/>
<ActionButton
@@ -348,6 +359,67 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
}
view={this.props.view}
/>
<ActionButton
key={"search"}
title={t("SEARCH")}
action={() => {
search(this.props.view);
}}
icon={ICONS.search}
view={this.props.view}
/>
<ActionButton
key={"ocr"}
title={t("RUN_OCR")}
action={(e:React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
if(!this.props.view.plugin.settings.taskboneEnabled) {
new Notice("Taskbone OCR is not enabled. Please go to plugins settings to enable it.",4000);
return;
}
this.props.view.plugin.taskbone.getTextForView(this.props.view, e[CTRL_OR_CMD]);
}}
icon={ICONS.ocr}
view={this.props.view}
/>
<ActionButton
key={"openLink"}
title={t("OPEN_LINK_CLICK")}
action={() => {
const event = new MouseEvent("click", {
ctrlKey: true,
metaKey: false,
shiftKey: false,
altKey: false,
});
this.props.view.handleLinkClick(this.props.view, event);
}}
icon={ICONS.openLink}
view={this.props.view}
/>
<ActionButton
key={"openLinkProperties"}
title={t("OPEN_LINK_PROPS")}
action={() => {
const event = new MouseEvent("click", {
ctrlKey: true,
metaKey: false,
shiftKey: true,
altKey: true,
});
this.props.view.handleLinkClick(this.props.view, event);
}}
icon={ICONS.openLinkProperties}
view={this.props.view}
/>
<ActionButton
key={"save"}
title={t("FORCE_SAVE")}
action={() => {
this.props.view.forceSave();
}}
icon={saveIcon(this.state.isDirty)}
view={this.props.view}
/>
</div>
</fieldset>
<fieldset>

147
src/ocr/Taskbone.ts Normal file
View File

@@ -0,0 +1,147 @@
import { createPNG, ExcalidrawAutomate } from "../ExcalidrawAutomate";
import {Notice, requestUrl} from "obsidian"
import ExcalidrawPlugin from "../main"
import {log} from "../utils/Utils"
import ExcalidrawView, { ExportSettings } from "../ExcalidrawView"
import FrontmatterEditor from "src/utils/Frontmatter";
import { ExcalidrawElement, ExcalidrawImageElement } from "@zsviczian/excalidraw/types/element/types";
import { EmbeddedFilesLoader } from "src/EmbeddedFileLoader";
const TASKBONE_URL = "https://api.taskbone.com/"; //"https://excalidraw-preview.onrender.com/";
const TASKBONE_OCR_FN = "execute?id=60f394af-85f6-40bc-9613-5d26dc283cbb";
export default class Taskbone {
get apiKey() {
return this.plugin.settings.taskboneAPIkey;
}
constructor(
private plugin: ExcalidrawPlugin
) {
}
public async initialize(save:boolean = true):Promise<string> {
if(this.plugin.settings.taskboneAPIkey !== "") return;
const response = await requestUrl({
url: `${TASKBONE_URL}users/excalidraw-obsidian/identities`,
method: "post",
contentType: "application/json",
throw: false
});
if(!response) return;
const apiKey = response.json?.apiKey;
if(apiKey && typeof apiKey === "string") {
if(save) await this.plugin.loadSettings();
this.plugin.settings.taskboneAPIkey = apiKey;
if(save) await this.plugin.saveSettings();
}
return apiKey;
}
public async getTextForView(view: ExcalidrawView, forceReScan: boolean) {
await view.forceSave(true);
const viewElements = view.excalidrawAPI.getSceneElements().filter((el:ExcalidrawElement) =>
el.type==="freedraw" ||
( el.type==="image" &&
!this.plugin.isExcalidrawFile(view.excalidrawData.getFile(el.fileId)?.file)
));
if(viewElements.length === 0) {
new Notice ("Aborting OCR because there are no image or freedraw elements on the canvas.",4000);
return;
}
const fe = new FrontmatterEditor(view.data);
if(fe.hasKey("taskbone-ocr") && !forceReScan) {
new Notice ("The drawing has already been processed, you will find the result in the frontmatter in markdown view mode. If you ran the command from the Obsidian Panel in Excalidraw then you can CTRL(CMD)+click the command to force the rescaning.",4000)
return;
}
const bb = this.plugin.ea.getBoundingBox(viewElements);
const size = (bb.width*bb.height);
const minRatio = Math.sqrt(360000/size);
const maxRatio = Math.sqrt(size/16000000);
const scale = minRatio > 1
? minRatio
: (
maxRatio > 1
? 1/maxRatio
: 1
);
const loader = new EmbeddedFilesLoader(
this.plugin,
false,
);
const exportSettings: ExportSettings = {
withBackground: true,
withTheme: true,
};
const img =
await createPNG(
view.file.path + "#^taskbone",
scale,
exportSettings,
loader,
"light",
null,
null,
[],
this.plugin,
0
);
const text = await this.getTextForImage(img);
if(text) {
fe.setKey("taskbone-ocr",text);
view.data = fe.data;
view.save(false);
window.navigator.clipboard.writeText(text);
new Notice("I placed the recognized in the drawing's frontmatter and onto the system clipboard.");
}
}
private async getTextForImage(image: Blob):Promise<string> {
const url = TASKBONE_URL+TASKBONE_OCR_FN;
if(this.apiKey === "") {
await this.initialize();
}
const base64Image = await this.blobToBase64(image);
const input = {
records: [{
image: base64Image
}]
};
const apiResponse = await requestUrl ({
url: url,
method: "post",
contentType: "application/json",
body: JSON.stringify(input),
headers: {
authorization: `Bearer ${this.apiKey}`
},
throw: false
});
const content = apiResponse?.json;
if(!content || apiResponse.status !== 200) {
new Notice("Something went wrong while processing your request. Please check developer console for more information");
log(apiResponse);
return;
}
return content.records[0].text;
}
private async blobToBase64(blob: Blob): Promise<string> {
const arrayBuffer = await blob.arrayBuffer()
const bytes = new Uint8Array(arrayBuffer)
var binary = '';
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
}

View File

@@ -28,6 +28,8 @@ export interface ExcalidrawSettings {
compress: boolean;
autosave: boolean;
autosaveInterval: number;
autosaveIntervalDesktop: number;
autosaveIntervalMobile: number;
drawingFilenamePrefix: string;
drawingEmbedPrefixWithFilename: boolean;
drawingFilnameEmbedPostfix: string;
@@ -43,6 +45,7 @@ export interface ExcalidrawSettings {
matchThemeTrigger: boolean;
defaultMode: string;
defaultPenMode: "never" | "mobile" | "always";
zoomToFitOnOpen: boolean;
zoomToFitOnResize: boolean;
zoomToFitMaxLevel: number;
openInAdjacentPane: boolean;
@@ -110,6 +113,8 @@ export interface ExcalidrawSettings {
showReleaseNotes: boolean;
showNewVersionNotification: boolean;
mathjaxSourceURL: string;
taskboneEnabled: boolean;
taskboneAPIkey: string;
}
export const DEFAULT_SETTINGS: ExcalidrawSettings = {
@@ -120,6 +125,8 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
compress: false,
autosave: true,
autosaveInterval: 15000,
autosaveIntervalDesktop: 15000,
autosaveIntervalMobile: 10000,
drawingFilenamePrefix: "Drawing ",
drawingEmbedPrefixWithFilename: true,
drawingFilnameEmbedPostfix: " ",
@@ -135,6 +142,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
matchThemeTrigger: false,
defaultMode: "normal",
defaultPenMode: "never",
zoomToFitOnOpen: true,
zoomToFitOnResize: true,
zoomToFitMaxLevel: 2,
linkPrefix: "📍",
@@ -193,10 +201,12 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
mdCSS: "",
scriptEngineSettings: {},
defaultTrayMode: false,
previousRelease: "1.6.13",
previousRelease: "0.0.0",
showReleaseNotes: true,
showNewVersionNotification: true,
mathjaxSourceURL: "https://cdn.jsdelivr.net/npm/mathjax@3.2.1/es5/tex-svg.js"
mathjaxSourceURL: "https://cdn.jsdelivr.net/npm/mathjax@3.2.1/es5/tex-svg.js",
taskboneEnabled: false,
taskboneAPIkey: "",
};
export class ExcalidrawSettingTab extends PluginSettingTab {
@@ -343,6 +353,8 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}),
);
this.containerEl.createEl("h1", { text: t("SAVING_HEAD") });
new Setting(containerEl)
.setName(t("COMPRESS_NAME"))
.setDesc(fragWithHTML(t("COMPRESS_DESC")))
@@ -353,7 +365,45 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
this.plugin.settings.compress = value;
this.applySettingsUpdate();
}),
);
);
new Setting(containerEl)
.setName(t("AUTOSAVE_INTERVAL_DESKTOP_NAME"))
.setDesc(fragWithHTML(t("AUTOSAVE_INTERVAL_DESKTOP_DESC")))
.addDropdown((dropdown) =>
dropdown
.addOption("15000", "Frequent (every 15 seconds)")
.addOption("60000", "Moderate (every 60 seconds)")
.addOption("300000", "Rare (every 5 minutes)")
.addOption("900000", "Practically never (every 15 minutes)")
.setValue(this.plugin.settings.autosaveIntervalDesktop.toString())
.onChange(async (value) => {
this.plugin.settings.autosaveIntervalDesktop = parseInt(value);
this.plugin.settings.autosaveInterval = app.isMobile
? this.plugin.settings.autosaveIntervalMobile
: this.plugin.settings.autosaveIntervalDesktop;
this.applySettingsUpdate();
}),
);
new Setting(containerEl)
.setName(t("AUTOSAVE_INTERVAL_MOBILE_NAME"))
.setDesc(fragWithHTML(t("AUTOSAVE_INTERVAL_MOBILE_DESC")))
.addDropdown((dropdown) =>
dropdown
.addOption("10000", "Frequent (every 10 seconds)")
.addOption("30000", "Moderate (every 30 seconds)")
.addOption("60000", "Rare (every 1 minute)")
.addOption("300000", "Practically never (every 5 minutes)")
.setValue(this.plugin.settings.autosaveIntervalMobile.toString())
.onChange(async (value) => {
this.plugin.settings.autosaveIntervalMobile = parseInt(value);
this.plugin.settings.autosaveInterval = app.isMobile
? this.plugin.settings.autosaveIntervalMobile
: this.plugin.settings.autosaveIntervalDesktop;
this.applySettingsUpdate();
}),
);
this.containerEl.createEl("h1", { text: t("FILENAME_HEAD") });
containerEl.createDiv("", (el) => {
@@ -540,6 +590,18 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}),
);
new Setting(containerEl)
.setName(t("ZOOM_TO_FIT_ONOPEN_NAME"))
.setDesc(fragWithHTML(t("ZOOM_TO_FIT_ONOPEN_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.zoomToFitOnOpen)
.onChange(async (value) => {
this.plugin.settings.zoomToFitOnOpen = value;
this.applySettingsUpdate();
}),
);
new Setting(containerEl)
.setName(t("ZOOM_TO_FIT_NAME"))
.setDesc(fragWithHTML(t("ZOOM_TO_FIT_DESC")))
@@ -1334,6 +1396,44 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
);
});
this.containerEl.createEl("h2", { text: t("TASKBONE_HEAD") });
this.containerEl.createEl("p", { text: t("TASKBONE_DESC") });
let taskboneAPIKeyText: TextComponent;
new Setting(containerEl)
.setName(t("TASKBONE_ENABLE_NAME"))
.setDesc(fragWithHTML(t("TASKBONE_ENABLE_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.taskboneEnabled)
.onChange(async (value) => {
taskboneAPIKeyText.setDisabled(!value);
this.plugin.settings.taskboneEnabled = value;
if(this.plugin.settings.taskboneAPIkey === "") {
const apiKey = await this.plugin.taskbone.initialize(false);
if(apiKey) {
taskboneAPIKeyText.setValue(apiKey);
}
}
this.applySettingsUpdate();
}),
);
new Setting(containerEl)
.setName(t("TASKBONE_APIKEY_NAME"))
.setDesc(fragWithHTML(t("TASKBONE_APIKEY_DESC")))
.addText((text) => {
taskboneAPIKeyText = text;
taskboneAPIKeyText
.setValue(this.plugin.settings.taskboneAPIkey)
.onChange(async (value) => {
this.plugin.settings.taskboneAPIkey = value;
this.applySettingsUpdate();
})
.setDisabled(!this.plugin.settings.taskboneEnabled);
}
);
//-------------------------------------
//Script settings
//-------------------------------------

42
src/utils/Frontmatter.ts Normal file
View File

@@ -0,0 +1,42 @@
// alternative https://github.com/OPD-libs/OPD-libs
export default class FrontmatterEditor {
private frontmatterStr:string;
private dataWOfrontmatter: string;
private initialized:boolean = false;
constructor (data:string) {
this.dataWOfrontmatter = data;
data = data.replaceAll("\r\n", "\n").replaceAll("\r", "\n");
const tmp = data.split(/^---(?:.|\n)*(?:^---\n)/gm);
if(tmp.length!==2) return;
this.dataWOfrontmatter = tmp[1];
this.frontmatterStr = data.match(/^---((?:.|\n)*)(?:^---\n)/gm)[0].replaceAll(/(^---\n|^\n)/gm,"").trim()+"\n";
this.initialized = true;
}
public hasKey(key:string):boolean {
if(!this.initialized) return false;
const reg = new RegExp(`^${key}:`,"gm");
return Boolean(this.frontmatterStr.match(reg));
}
public setKey(key:string, value:string) {
if(!this.initialized) return;
value = value.replaceAll("\r\n", "\n").replaceAll("\r", "\n").replaceAll(":",";").trim().split("\n").join(" ");
if(this.hasKey(key)) {
const reg = new RegExp(`^${key}:.*\\n(?:\\s\\s.*\\n)*`,"gm");
this.frontmatterStr =
this.frontmatterStr.split(reg).join("\n").trim() +
`\n${key}: ${value}`;
return;
}
this.frontmatterStr = this.frontmatterStr.trim()+`\n${key}: ${value}`;
}
get data() {
if(!this.initialized) return this.dataWOfrontmatter;
return ["---",this.frontmatterStr,"---",this.dataWOfrontmatter].join("\n");
}
}

View File

@@ -362,7 +362,10 @@ export const getImageSize = async (
): Promise<{ height: number; width: number }> => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve({ height: img.naturalHeight, width: img.naturalWidth });
img.onload = () => {
//console.log({ height: img.naturalHeight, width: img.naturalWidth, img});
resolve({ height: img.naturalHeight, width: img.naturalWidth });
};
img.onerror = reject;
img.src = src;
});
@@ -597,6 +600,7 @@ export const getEmbeddedFilenameParts = (fname:string):{
filepath: string,
hasBlockref: boolean,
hasGroupref: boolean,
hasTaskbone: boolean,
hasArearef: boolean,
blockref: string,
hasSectionref: boolean,
@@ -604,13 +608,14 @@ export const getEmbeddedFilenameParts = (fname:string):{
linkpartReference: string,
linkpartAlias: string
} => {
// 0 1 23 4 5 6 7 8 9
const parts = fname?.match(/([^#\^]*)((#\^)(group=|area=)?([^\|]*)|(#)(group=|area=)?([^\^\|]*))(.*)/);
// 0 1 23 4 5 6 7 8 9
const parts = fname?.match(/([^#\^]*)((#\^)(group=|area=|taskbone)?([^\|]*)|(#)(group=|area=|taskbone)?([^\^\|]*))(.*)/);
if(!parts) {
return {
filepath: fname,
hasBlockref: false,
hasGroupref: false,
hasTaskbone: false,
hasArearef: false,
blockref: "",
hasSectionref: false,
@@ -623,6 +628,7 @@ export const getEmbeddedFilenameParts = (fname:string):{
filepath: parts[1],
hasBlockref: Boolean(parts[3]),
hasGroupref: (parts[4]==="group=") || (parts[7]==="group="),
hasTaskbone: (parts[4]==="taskbone") || (parts[7]==="taskbone"),
hasArearef: (parts[4]==="area=") || (parts[7]==="area="),
blockref: parts[5],
hasSectionref: Boolean(parts[6]),

View File

@@ -268,7 +268,7 @@ textarea.excalidraw-wysiwyg {
}
.excalidraw .ToolIcon__keybinding {
font-size: 0.5rem;
font-size: 0.45rem !important;
}
.Island > .Stack > .Stack {
@@ -284,4 +284,8 @@ label.color-input-container > input {
top: 10px !important;
right: 10px !important;
bottom: 10px !important;
}
.excalidraw-hidden {
display: none !important;
}

View File

@@ -3,7 +3,7 @@
"baseUrl": ".",
"sourceMap": true,
"module": "es2015",
"target": "es2017",
"target": "es2017", //script engine requires for async execution
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
@@ -12,7 +12,7 @@
"lib": [
"dom",
"scripthost",
"es2017",
"es2015",
"esnext",
"DOM.Iterable"
],

View File

@@ -11,7 +11,7 @@
"lib": [
"dom",
"scripthost",
"es2017",
"es2015",
"esnext",
"DOM.Iterable"
],

View File

@@ -978,7 +978,7 @@
"@babel/types" "^7.4.4"
"esutils" "^2.0.2"
"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.16.0", "@babel/preset-react@^7.16.7":
"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.16.0", "@babel/preset-react@^7.18.6":
"integrity" "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg=="
"resolved" "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz"
"version" "7.18.6"
@@ -1866,14 +1866,14 @@
"resolved" "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz"
"version" "1.2.4"
"@types/react-dom@^17.0.2":
"integrity" "sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg=="
"resolved" "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.17.tgz"
"version" "17.0.17"
"@types/react-dom@^18.0.9":
"integrity" "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg=="
"resolved" "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz"
"version" "18.0.9"
dependencies:
"@types/react" "^17"
"@types/react" "*"
"@types/react@^17":
"@types/react@*":
"integrity" "sha512-Xt40xQsrkdvjn1EyWe1Bc0dJLcil/9x2vAuW7ya+PuQip4UYUaXyhzWmAbwRsdMgwOFHpfp7/FFZebDU6Y8VHA=="
"resolved" "https://registry.npmjs.org/@types/react/-/react-17.0.2.tgz"
"version" "17.0.2"
@@ -7450,14 +7450,13 @@
"strip-ansi" "^6.0.1"
"text-table" "^0.2.0"
"react-dom@^17.0.2", "react-dom@^17.0.2 || ^18.2.0":
"integrity" "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA=="
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
"version" "17.0.2"
"react-dom@^17.0.2 || ^18.2.0", "react-dom@^18.2.0":
"integrity" "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
"version" "18.2.0"
dependencies:
"loose-envify" "^1.1.0"
"object-assign" "^4.1.1"
"scheduler" "^0.20.2"
"scheduler" "^0.23.0"
"react-error-overlay@^6.0.11":
"integrity" "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg=="
@@ -7534,13 +7533,12 @@
optionalDependencies:
"fsevents" "^2.3.2"
"react@^17.0.2", "react@^17.0.2 || ^18.2.0", "react@>= 16", "react@17.0.2":
"integrity" "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA=="
"resolved" "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
"version" "17.0.2"
"react@^17.0.2 || ^18.2.0", "react@^18.2.0", "react@>= 16":
"integrity" "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="
"resolved" "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
"version" "18.2.0"
dependencies:
"loose-envify" "^1.1.0"
"object-assign" "^4.1.1"
"readable-stream@^2.0.1":
"integrity" "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw=="
@@ -7875,13 +7873,12 @@
dependencies:
"xmlchars" "^2.2.0"
"scheduler@^0.20.2":
"integrity" "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ=="
"resolved" "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz"
"version" "0.20.2"
"scheduler@^0.23.0":
"integrity" "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw=="
"resolved" "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz"
"version" "0.23.0"
dependencies:
"loose-envify" "^1.1.0"
"object-assign" "^4.1.1"
"schema-utils@^2.6.5":
"integrity" "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg=="