Compare commits

..

416 Commits

Author SHA1 Message Date
Zsolt Viczian
2d16b59ea3 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2022-01-07 19:34:46 +01:00
Zsolt Viczian
b292ca0fa3 1.5.15 2022-01-07 19:34:24 +01:00
zsviczian
95c21c2d5e Update README.md 2022-01-06 15:25:27 +01:00
zsviczian
7e45a0f952 Update index.md 2022-01-06 07:50:20 +01:00
zsviczian
7c8460646a Merge pull request #343 from 1-2-3/master
Add modify background color opacity scripts image
2022-01-06 07:47:44 +01:00
zahuifan
8badc3eb8f Add modify background color opacity scripts image 2022-01-06 10:10:19 +08:00
Zsolt Viczian
ad98d114e1 scriptSettings 2022-01-05 21:09:36 +01:00
Zsolt Viczian
c90370a606 1.5.14 2022-01-03 22:43:30 +01:00
Zsolt Viczian
9889567798 update Dimensions 2022-01-03 22:24:03 +01:00
Zsolt Viczian
70ee82bdb1 check github for updates 2022-01-03 21:09:45 +01:00
zsviczian
4effb42762 Merge pull request #339 from 1-2-3/master
Fixed 3 scripts that did not handle NaN input values
2022-01-03 18:16:28 +01:00
zahuifan
176248f33e Fix fixed spacing ea-script not processing NaN. 2022-01-03 21:37:44 +08:00
zahuifan
dbb64e5044 fix modify opacity ea-script not processing NaN. 2022-01-03 21:32:43 +08:00
Zsolt Viczian
be7c043871 added video to OCR description 2022-01-02 22:16:02 +01:00
Zsolt Viczian
ba69f4319f 1.5.13 2022-01-02 22:13:00 +01:00
Zsolt Viczian
5b755db673 updated index.md 2022-01-02 20:43:30 +01:00
Zsolt Viczian
2e0ce819a9 index 2022-01-02 19:45:40 +01:00
Zsolt Viczian
be03026360 . 2022-01-02 19:41:07 +01:00
Zsolt Viczian
d0385563e2 image 2022-01-02 19:33:45 +01:00
Zsolt Viczian
91e84cc41a image 2022-01-02 19:33:12 +01:00
Zsolt Viczian
f308cfe907 index.md update 2022-01-02 19:09:40 +01:00
Zsolt Viczian
0c4919547f . 2022-01-02 19:08:37 +01:00
Zsolt Viczian
c0eb85abf5 updated css, index.md, modal css class 2022-01-02 19:00:50 +01:00
Zsolt Viczian
73b31627f3 typo 2022-01-02 18:33:16 +01:00
Zsolt Viczian
241a1c7301 css 2022-01-02 18:31:57 +01:00
Zsolt Viczian
4182098730 Plugin store MVP 2022-01-02 18:20:53 +01:00
Zsolt Viczian
389387aa6e css 2022-01-02 16:09:12 +01:00
Zsolt Viczian
381401f175 scriptEngineInstall WIP 2022-01-02 15:59:14 +01:00
Zsolt Viczian
cca4158295 added index 2022-01-02 12:53:52 +01:00
Zsolt Viczian
d4ebf68bb5 added set link alias script 2022-01-01 19:26:40 +01:00
Zsolt Viczian
c9b9b64513 1.5.12 2022-01-01 18:08:05 +01:00
Zsolt Viczian
ea202763be typo 2022-01-01 14:21:56 +01:00
Zsolt Viczian
1c86308ee3 typos 2022-01-01 14:20:51 +01:00
Zsolt Viczian
66936975dd new scripts 2022-01-01 14:16:01 +01:00
Zsolt Viczian
d70c290658 delete from filesMaster if file no longer exists 2022-01-01 13:49:03 +01:00
zsviczian
be45a0dfb6 Update README.md 2022-01-01 08:23:12 +01:00
zsviczian
98a76d464b Update README.md 2022-01-01 08:07:08 +01:00
zsviczian
2edd25c298 Merge pull request #334 from 1-2-3/master
Update ea-script readme.md
2022-01-01 08:02:14 +01:00
1-2-3
ca7d9576b4 Update README.md 2022-01-01 01:10:24 +08:00
1-2-3
110cb60e00 Update README.md 2022-01-01 01:08:19 +08:00
zsviczian
83764410f0 Merge pull request #333 from 1-2-3/master
Add ea-scripts demo image files
2021-12-31 16:27:11 +01:00
1-2-3
c7154d531f Update Fixed vertical distance.md 2021-12-31 19:19:56 +08:00
1-2-3
aafedba989 Update Fixed spacing.md 2021-12-31 19:18:54 +08:00
1-2-3
9269b52057 Add ea-scripts demo image files 2021-12-31 19:16:44 +08:00
Zsolt Viczian
1ce44c2d55 1.5.11 fix for #327 2021-12-30 19:15:34 +01:00
Zsolt Viczian
1796402ced readme 2021-12-30 14:41:35 +01:00
Zsolt Viczian
f350895817 escape | 2021-12-30 14:37:45 +01:00
Zsolt Viczian
46db9ccbbf added contributor to readme 2021-12-30 14:35:00 +01:00
Zsolt Viczian
1123a3bd81 ea-script readme 2021-12-30 14:29:54 +01:00
zsviczian
79c62edbe7 Update Create new markdown file and embed into active drawing.md 2021-12-30 13:22:15 +01:00
Zsolt Viczian
76faf3011b 1.5.10 2021-12-30 13:20:51 +01:00
zsviczian
d0d6fbad12 Merge pull request #330 from 1-2-3/master
Add Elbow connectors ea-scripts file
2021-12-30 13:06:42 +01:00
zsviczian
3ba6292d6f Update Elbow connectors.md 2021-12-30 13:06:24 +01:00
zahuifan
adad32b641 Add Darken and Lighten background color ea-scripts 2021-12-30 19:31:48 +08:00
zahuifan
35bb2368fe Add Elbow connectors ea-scripts file 2021-12-30 08:48:33 +08:00
Zsolt Viczian
2c63a24c81 savePNG, saveSVG, and CreateNewDrawing refactored 2021-12-29 22:15:56 +01:00
Zsolt Viczian
db17b91418 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2021-12-29 10:42:25 +01:00
Zsolt Viczian
47b9b16588 1.5.8 2021-12-29 10:42:11 +01:00
zsviczian
5194ced50c Update Zoom to Fit Selected Elements.md 2021-12-29 10:36:42 +01:00
Zsolt Viczian
9eaf22305a improved scripts, addText solved for non-container bound 2021-12-29 10:33:03 +01:00
Zsolt Viczian
6392bcd06e added ea-script 2021-12-28 09:34:02 +01:00
Zsolt Viczian
b6c5bfb20a Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2021-12-27 18:45:51 +01:00
Zsolt Viczian
4328537034 1.5.7 2021-12-27 18:45:34 +01:00
zsviczian
49f7c47064 Merge pull request #322 from 1-2-3/master
Add expand rectangles ea-script files
2021-12-27 15:32:23 +01:00
zahuifan
fec9d083e7 Add expand rectangles ea-script files 2021-12-27 20:20:39 +08:00
zsviczian
b8374a6b0b Merge pull request #319 from 1-2-3/master
Add Fixed spacing etc. ea-scripts files
2021-12-26 08:13:13 +01:00
zahuifan
a30c6bbf48 Modify the description of ea-Scripts 2021-12-26 10:57:29 +08:00
zahuifan
f85246f894 Add Fixed spacing etc. ea-scripts files 2021-12-26 10:49:10 +08:00
Zsolt Viczian
585640ff2e 1.5.6 2021-12-25 18:05:00 +01:00
Zsolt Viczian
17f6c7d2ac 1.5.5 2021-12-25 16:41:26 +01:00
Zsolt Viczian
896a31d02a Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2021-12-25 16:14:24 +01:00
Zsolt Viczian
c54c133ba0 Fix link click for container with text did not work 2021-12-25 16:14:06 +01:00
zsviczian
7c01da8731 Merge pull request #315 from 1-2-3/master
Add Modify background color opacity ea-scripts file
2021-12-25 16:12:45 +01:00
zahuifan
489b53f0f6 Add opacity ea-scripts file 2021-12-25 19:44:51 +08:00
Zsolt Viczian
73dd39905e strokeStyle spelling error 2021-12-25 11:45:08 +01:00
Zsolt Viczian
2e843f65ed fixed special chars in transclusion block ref 2021-12-25 11:12:59 +01:00
Zsolt Viczian
b18ddc6407 1.5.4 2021-12-23 20:23:09 +01:00
Zsolt Viczian
2359dd7f56 many small bugs...mostly container bound text 2021-12-23 18:49:41 +01:00
Zsolt Viczian
060e86d7ff Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2021-12-21 15:47:04 +01:00
Zsolt Viczian
3995e792fe container bound text element v1 2021-12-21 15:46:35 +01:00
zsviczian
68391e5163 Update ExcalidrawScriptsEngine.md 2021-12-17 08:17:34 +01:00
zsviczian
cce4475577 Update ExcalidrawScriptsEngine.md 2021-12-17 08:15:01 +01:00
zsviczian
73c8b1aa33 Add files via upload 2021-12-15 17:36:37 +01:00
zsviczian
b8d0b47a9d Rename Stroke Width.md to Modify stroke width of selected elements.md 2021-12-15 16:26:44 +01:00
zsviczian
0684ff13cc Update ExcalidrawScriptsEngine.md 2021-12-15 16:21:42 +01:00
zsviczian
1b28cd0e82 Update ExcalidrawScriptsEngine.md 2021-12-15 16:18:48 +01:00
zsviczian
09e8e64a2f Add files via upload 2021-12-15 16:16:30 +01:00
zsviczian
b166d3cef9 Add files via upload 2021-12-15 16:14:53 +01:00
zsviczian
06c3ba0b8f Update README.md 2021-12-14 10:22:05 +01:00
Zsolt Viczian
ba7c39be74 lint 2021-12-13 18:33:46 +01:00
Zsolt Viczian
a844244450 1.5.3 multi-selection line edit 2021-12-13 18:27:26 +01:00
Zsolt Viczian
bd6f9b7a1d 1.5.2 2021-12-12 19:01:16 +01:00
Zsolt Viczian
6b87016cb3 formatting 2021-12-12 15:29:49 +01:00
Zsolt Viczian
e632a3c665 Font Family script 2021-12-12 15:24:54 +01:00
Zsolt Viczian
d2b25441c3 text-align script 2021-12-12 15:14:25 +01:00
Zsolt Viczian
41cca8e68d correct codeblock 2021-12-12 13:53:40 +01:00
Zsolt Viczian
ca3394a2fc correct codeblock 2021-12-12 13:52:29 +01:00
Zsolt Viczian
daeb61e858 split lines 2021-12-12 13:51:24 +01:00
Zsolt Viczian
c39ff3f3e2 1.5.1 2021-12-12 13:44:36 +01:00
zsviczian
f4a045b476 Update ExcalidrawScriptsEngine.md 2021-12-12 10:34:27 +01:00
Zsolt Viczian
d7f8429d91 updated script engine examples 2021-12-12 10:31:26 +01:00
Zsolt Viczian
d4c16b7d04 added thumbnail image to scripts 2021-12-12 10:27:40 +01:00
Zsolt Viczian
e4f8506d24 . 2021-12-12 08:16:52 +01:00
Zsolt Viczian
4f82b5cb0d updated formatting 2021-12-12 08:16:16 +01:00
Zsolt Viczian
8298434f80 added bullet point script 2021-12-12 08:15:17 +01:00
Zsolt Viczian
7336936fca added image 2021-12-12 08:09:31 +01:00
Zsolt Viczian
5692006d19 script changes 2021-12-12 07:43:27 +01:00
Zsolt Viczian
9cd82dcd2e 1.5.0 2021-12-11 17:44:16 +01:00
Zsolt Viczian
e6f011b641 script samples 2021-12-11 13:48:41 +01:00
Zsolt Viczian
70f7f0d938 script samples 2021-12-11 13:47:09 +01:00
Zsolt Viczian
3f5389cf29 Sample scripts 2021-12-11 13:45:05 +01:00
zsviczian
c126920f25 Update readme.md 2021-12-11 12:03:57 +01:00
zsviczian
8c1521be71 Update introduction.md 2021-12-11 11:58:49 +01:00
zsviczian
3c00fcaf80 Update introduction.md 2021-12-11 11:57:06 +01:00
zsviczian
53f7353fa3 Update introduction.md 2021-12-11 11:56:26 +01:00
zsviczian
446e8b12c5 Update introduction.md 2021-12-11 11:56:03 +01:00
zsviczian
78a5320df4 Update readme.md 2021-12-11 11:54:38 +01:00
zsviczian
1a3b206398 Update readme.md 2021-12-11 11:54:22 +01:00
zsviczian
1014934604 Update ExcalidrawScriptsEngine.md 2021-12-11 11:52:35 +01:00
zsviczian
dd92b3b6d5 Update ExcalidrawScriptsEngine.md 2021-12-11 11:47:33 +01:00
zsviczian
f9f2f6425c Set theme jekyll-theme-hacker 2021-12-11 11:34:41 +01:00
zsviczian
1a7f133773 Set theme jekyll-theme-midnight 2021-12-11 11:34:21 +01:00
zsviczian
179882543a Set theme jekyll-theme-hacker 2021-12-11 11:28:25 +01:00
Zsolt Viczian
45988df24a scripts and documentation 2021-12-11 11:23:37 +01:00
Zsolt Viczian
a0135b5942 ScriptsEngine, Prompt, async, settings 2021-12-09 21:04:52 +01:00
Zsolt Viczian
9a8376bd93 lint fixes 2021-12-09 19:02:22 +01:00
Zsolt Viczian
b8af6e1447 eslint --fix warnings 2021-12-08 20:12:08 +01:00
Zsolt Viczian
a42f853c00 eslint -fix resolve warnings 2021-12-08 20:08:21 +01:00
Zsolt Viczian
581013b1b3 eslint --fix 2021-12-08 20:05:42 +01:00
Zsolt Viczian
4181c1e3f7 fixed heading ref when heading contains a link 2021-12-07 18:46:29 +01:00
Zsolt Viczian
c6bd787700 script engine alpha 2021-12-06 22:38:11 +01:00
Zsolt Viczian
fd8128599c doc update 2021-12-05 20:25:11 +01:00
Zsolt Viczian
c324ed9618 1.4.19 2021-12-05 18:38:44 +01:00
Zsolt Viczian
cc7227d164 1.4.18 2021-12-04 16:38:21 +01:00
zsviczian
c75e7fb76c Merge pull request #293 from qifei9/patch-1
Update zh-cn.ts
2021-12-04 13:15:21 +01:00
Zsolt Viczian
6d3eb20ff1 check if exists before adding to embeddedFiles 2021-12-04 13:14:39 +01:00
qifei9
a603e4eeac Update zh-cn.ts
Fix the translation of `COMPATIBILITY_MODE_DESC` of zh-cn
2021-12-04 19:44:59 +08:00
zsviczian
ac260925dd Update ExcalidrawView.ts 2021-12-03 12:51:15 +01:00
Zsolt Viczian
1a2e7ac23f 1.4.17 2021-12-01 22:05:09 +01:00
Zsolt Viczian
1eb9b88f4b updated readme 2021-11-30 20:12:05 +01:00
Zsolt Viczian
a7814f383a 1.4.16 2021-11-30 19:57:50 +01:00
zsviczian
54e6d47df0 Update README.md 2021-11-30 14:59:19 +01:00
zsviczian
55ea1cf121 Update README.md 2021-11-30 14:57:02 +01:00
zsviczian
a2982f3406 Update EmbeddedFileLoader.ts 2021-11-30 14:52:21 +01:00
zsviczian
633ff1fea8 #286
https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/286#issuecomment-982179639
2021-11-30 08:46:05 +01:00
Zsolt Viczian
3312df0743 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2021-11-29 22:03:30 +01:00
Zsolt Viczian
0722bb8133 1.4.15 2021-11-29 22:02:59 +01:00
zsviczian
c9be4d95d7 Update README.md 2021-11-29 14:46:47 +01:00
Zsolt Viczian
023ddcec39 en.ts edited 2021-11-28 21:09:54 +01:00
zsviczian
9255643646 Update README.md 2021-11-28 21:09:04 +01:00
Zsolt Viczian
36ead43102 1.4.14 2021-11-28 20:54:51 +01:00
zsviczian
f61d000326 Update README.md 2021-11-28 07:52:44 +01:00
zsviczian
9bb254dc48 Update README.md 2021-11-28 07:42:21 +01:00
Zsolt Viczian
5023ed46f5 1.4.13: Markdown embed 2021-11-27 21:31:46 +01:00
Zsolt Viczian
73616e5084 debugging file update race condition 2021-11-27 18:29:56 +01:00
Zsolt Viczian
f7bbe2e446 svgMarkdownMVP 2021-11-26 20:20:08 +01:00
Zsolt Viczian
94c3011435 invers image cache 2021-11-24 21:30:59 +01:00
Zsolt Viczian
b348def6f6 1.4.12 - fixed lib export + preview without theme 2021-11-22 18:57:18 +01:00
Zsolt Viczian
d57c59e9eb markdown render 2021-11-21 22:46:37 +01:00
zsviczian
3ed25c221e Merge pull request #263 from LukeWeidenwalker/fix-typos
Fix three typos in English localization
2021-11-21 19:26:58 +01:00
Zsolt Viczian
e0895d00ae 1.4.11 2021-11-21 19:18:16 +01:00
Zsolt Viczian
9e5cce2f6f Image inversion solved 2021-11-21 15:01:24 +01:00
Lukas Weidenholzer
200c2631cb fix three typos in English localization 2021-11-21 11:07:34 +01:00
Zsolt Viczian
272204263d svg improvements WIP 2021-11-20 15:29:56 +01:00
Zsolt Viczian
16b5472024 cleanup exportToBlob, CTRL_OR_CMD 2021-11-20 10:43:54 +01:00
zsviczian
d6a4350e1e Merge pull request #257 from JaspalSuri/patch-1
fix: correct some spelling mistakes
2021-11-19 11:18:03 +01:00
Jaspal Suri
3602fbb807 fix: correct some spelling mistakes
This is not a comprehensive review.
2021-11-18 23:14:13 -08:00
Zsolt Viczian
ecce315924 1.4.10 2021-11-16 21:44:23 +01:00
Zsolt Viczian
eee470dad5 corrected message 2021-11-15 22:08:00 +01:00
Zsolt Viczian
636c4bcafe 1.4.9 2021-11-15 22:05:44 +01:00
zsviczian
4eb2b293e2 embed excalidraw.md instead of svg or png 2021-11-15 14:12:38 +01:00
Zsolt Viczian
08528d9a88 1.4.8-beta-2 2021-11-14 23:13:06 +01:00
Zsolt Viczian
3e9ef99226 Reworked recursive image loading 2021-11-14 20:12:11 +01:00
Zsolt Viczian
f50ecd95c3 removed SVG snapshot 2021-11-14 12:08:19 +01:00
Zsolt Viczian
8b477a0e16 Added missing scale to createPNG interface 2021-11-14 09:41:53 +01:00
Zsolt Viczian
78891a1065 1.4.8-beta 2021-11-10 23:05:13 +01:00
Zsolt Viczian
b428cb7eed 1.4.7 (embed Excalidraw into Excalidraw fixed) 2021-11-08 19:44:45 +01:00
Zsolt Viczian
b20c1bed5a 1.4.6 2021-11-02 21:51:04 +01:00
Zsolt Viczian
f24c41eace 1.4.5 2021-11-02 21:33:11 +01:00
Zsolt Viczian
d33cf5ddd5 1.4.4 - basic copy/paste for equations & images 2021-11-01 17:41:12 +01:00
Zsolt Viczian
41491079be minapp version 12.16 2021-11-01 14:17:37 +01:00
Zsolt Viczian
5345c63672 updated versions 2021-11-01 14:17:05 +01:00
Zsolt Viczian
06acf09a85 1.4.3 2021-11-01 14:12:44 +01:00
Zsolt Viczian
ce6d983b38 LaTex MVP ready 2021-11-01 10:27:58 +01:00
zsviczian
bb6c0b54ff added Excalidraw files to filter 2021-10-30 21:54:51 +02:00
zsviczian
571dae52d3 Update Utils.ts 2021-10-29 19:51:00 +02:00
Zsolt Viczian
e6b5b0d125 latex WIP 2021-10-29 11:01:08 +02:00
Zsolt Viczian
8a1cf72095 1.4.2 2021-10-28 23:55:03 +02:00
Zsolt Viczian
f02425dcac 1.4.1 2021-10-26 23:06:00 +02:00
Zsolt Viczian
6e3cf60eab 1.4.0 2021-10-24 20:40:36 +02:00
zsviczian
32fdbf9dc2 Update README.md 2021-10-24 20:29:43 +02:00
Zsolt Viczian
1aed684ebe Fixed Mac CTRL vs. CMD button issue 2021-10-24 20:07:22 +02:00
Zsolt Viczian
1ad791d9bc 1.4.0 pre-release 2 2021-10-24 13:40:11 +02:00
zsviczian
bb9925024d Merge pull request #209 from zsviczian/Image-Element
Image element
2021-10-24 06:56:12 +02:00
Zsolt Viczian
e676255d69 switched excalidraw package to: 0.10.0-obsidian-2 2021-10-24 06:52:09 +02:00
Zsolt Viczian
691c60be24 1.4.0 pre-release 2021-10-23 19:35:40 +02:00
Zsolt Viczian
f4a458061a Image click navigation. Image embeds finalized. 2021-10-23 14:32:05 +02:00
Zsolt Viczian
c88c898f4a ExcalidrawRef readypromise 2021-10-23 09:14:07 +02:00
Zsolt Viczian
d2da408a59 Before implementing readyPromise for excalidrawRef 2021-10-23 08:56:51 +02:00
Zsolt Viczian
3b9a6404c5 integrate image element mid way 2021-10-22 20:34:27 +02:00
Zsolt Viczian
d9306922c3 save image to vault 2021-10-19 22:59:31 +02:00
zsviczian
578cc7a99c Merge pull request #201 from zsviczian/tmp
minor performance tweek
2021-10-19 20:03:36 +02:00
zsviczian
aa9f9ba91f Merge branch 'Image-Element' into tmp 2021-10-19 20:03:29 +02:00
Zsolt Viczian
b9251d4f1d minor performance tweek 2021-10-19 19:45:32 +02:00
Zsolt Viczian
13a980afed added mimetype 2021-10-19 18:40:20 +02:00
zsviczian
c911e0118f Merge pull request #199 from zsviczian/tmp
1.3.20
2021-10-18 20:33:43 +02:00
zsviczian
eca02a5941 Merge branch 'Image-Element' into tmp 2021-10-18 20:33:36 +02:00
Zsolt Viczian
9a57db43f2 1.3.20 2021-10-18 20:14:39 +02:00
Zsolt Viczian
f6b65ac3e9 bump image-support version 2021-10-18 18:50:16 +02:00
zsviczian
929348b390 Merge pull request #190 from zsviczian/temp
1.3.19
2021-10-12 20:50:48 +02:00
zsviczian
57f1b9f8da Merge branch 'Image-Element' into temp 2021-10-12 20:50:39 +02:00
Zsolt Viczian
fed106c811 1.3.19 2021-10-12 20:26:23 +02:00
Zsolt Viczian
739e919a43 mid way - adding Embedded files 2021-10-12 18:15:14 +02:00
zsviczian
e85cf4e196 Merge pull request #186 from zsviczian/temp-master
merge master into image-element
2021-10-11 19:59:18 +02:00
zsviczian
0c42353fce Merge branch 'Image-Element' into temp-master 2021-10-11 19:59:10 +02:00
Zsolt Viczian
7ebdec7713 1.3.18 fixed link hover and textElement rotate 2021-10-10 20:18:17 +02:00
Zsolt Viczian
1917dad8cd onKeyDown to reject events except from canvas 2021-10-10 18:10:35 +02:00
Zsolt Viczian
3100e2d70f update en.ts 2021-10-10 18:04:57 +02:00
zsviczian
7712cd49b6 Merge pull request #174 from zsviczian/temp-master
fetch upstream
2021-10-04 21:43:40 +02:00
zsviczian
856573763e Merge branch 'Image-Element' into temp-master 2021-10-04 21:43:28 +02:00
Zsolt Viczian
3bbff7f8d5 1.3.17 2021-10-04 21:28:09 +02:00
Zsolt Viczian
034927ada0 resolves #142 2021-10-04 21:14:31 +02:00
Zsolt Viczian
0cccdad13f implemented openInAdjacentLeaf #156 2021-10-04 19:45:18 +02:00
Zsolt Viczian
fe7f3f58c5 Update yarn.lock 2021-10-04 19:09:26 +02:00
zsviczian
48fd854944 Merge pull request #173 from zsviczian/tempmaster
updated package json with new libraries
2021-10-04 19:06:56 +02:00
zsviczian
8f9746393f Merge branch 'Image-Element' into tempmaster 2021-10-04 19:06:28 +02:00
Zsolt Viczian
23da271b73 updated package json with new libraries 2021-10-04 18:57:27 +02:00
Zsolt Viczian
627775c6c3 re-applied image element changes 2021-10-04 18:47:49 +02:00
Zsolt Viczian
59db43c3f0 resolves #172 and #166 2021-10-04 18:34:02 +02:00
Zsolt Viczian
597ee4f70e Revert "Excalidraw Image Element Demo"
This reverts commit 78fb37b173.
2021-10-04 18:25:35 +02:00
Zsolt Viczian
8222d8c146 Revert "embed Excalidraw into document"
This reverts commit f785d756be.
2021-10-04 18:25:27 +02:00
Zsolt Viczian
f785d756be embed Excalidraw into document 2021-10-04 18:22:47 +02:00
Zsolt Viczian
78fb37b173 Excalidraw Image Element Demo 2021-10-02 17:34:13 +02:00
Zsolt Viczian
a17638717f api documentation updated 2021-10-01 21:01:11 +02:00
Zsolt Viczian
70de8ba2f8 1.3.16 2021-10-01 20:59:02 +02:00
Zsolt Viczian
e8a29a2715 1.3.15 2021-09-29 06:50:51 +02:00
Zsolt Viczian
7b1f13391c 1.3.14 rawText copy/paste 2021-09-28 22:58:02 +02:00
Zsolt Viczian
33081b1a84 1.3.13 2021-09-27 19:45:05 +02:00
Zsolt Viczian
aafd9f17f8 1.3.12 2021-09-25 19:56:46 +02:00
zsviczian
a27da5f5f5 Update README.md 2021-09-23 12:36:06 +02:00
Zsolt Viczian
472b58a417 1.3.11 2021-09-21 19:12:55 +02:00
zsviczian
1bba254eaf Update README.md 2021-09-19 15:55:31 +02:00
Zsolt Viczian
a0e47c390c 1.3.10 Deployed improved Excalidraw freehand 2021-09-19 08:18:24 +02:00
Zsolt Viczian
caa1281d23 1.3.9 2021-09-17 21:54:01 +02:00
Zsolt Viczian
3fb2cbba14 1.3.8 2021-09-16 20:39:04 +02:00
Zsolt Viczian
8f1ec2acbc addBlob scale fixed 2021-09-14 22:52:08 +02:00
Zsolt Viczian
3e2d85dd09 ExcalidrawAutomate blob,line,scale 2021-09-14 22:38:03 +02:00
Zsolt Viczian
ec3e9fd5b7 1.3.7 kanban support, onDrop fix 2021-09-12 17:16:50 +02:00
Zsolt Viczian
784a7cb6bf 1.3.6 accept text drop, improved addElement 2021-09-11 12:07:30 +02:00
Zsolt Viczian
e8666797d7 1.3.5 2021-09-10 16:16:13 +02:00
zsviczian
5c2c1ebf5e added error messages 2021-09-09 10:46:14 +02:00
Zsolt Viczian
ecd19dd072 1.3.4 ea.viewToggleFullScreen() 2021-09-09 06:31:36 +02:00
Zsolt Viczian
377927c891 1.3.3 force-overflow text wrapping 2021-09-05 22:01:22 +02:00
Zsolt Viczian
9f28c974f7 1.3.2 - fixed EA.create() 2021-09-05 08:02:46 +02:00
Zsolt Viczian
1b9fa6a790 1.3.1 Drag&Drop + zoomToFit + no chunking 2021-09-04 23:37:43 +02:00
Zsolt Viczian
4dd8271223 zoomToFit change 2021-09-04 17:46:06 +02:00
Zsolt Viczian
3e62cd33a2 documentation improved 2021-09-03 19:58:51 +02:00
Zsolt Viczian
c670ecb09c 1.3.0 2021-09-03 18:47:25 +02:00
zsviczian
f5307db33e Update introduction.md 2021-09-03 09:30:36 +02:00
zsviczian
7e216306c0 Update introduction.md 2021-09-03 09:30:05 +02:00
zsviczian
e3daf5d22e Update introduction.md 2021-09-03 09:29:37 +02:00
zsviczian
a85a46cbae Update introduction.md 2021-09-03 09:27:52 +02:00
Zsolt Viczian
ac16f5427b Excalidraw Automate enhancements 2021-09-03 09:26:24 +02:00
Zsolt Viczian
edc92472df addElementsToView() 2021-09-01 22:20:07 +02:00
Zsolt Viczian
e66b7aef7f 1.2.24 gridSize 2021-08-31 20:40:25 +02:00
Zsolt Viczian
7e9d5e8867 1.2.23 2021-08-29 18:58:56 +02:00
Zsolt Viczian
167918f718 published wrapText to Excalidraw Automate 2021-08-29 11:25:02 +02:00
Zsolt Viczian
65af29c2ef Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2021-08-29 10:31:59 +02:00
Zsolt Viczian
b19e1b6dcb 1.2.22 2021-08-29 10:31:35 +02:00
zsviczian
f7263543fa Update README.md 2021-08-28 16:31:41 +02:00
Zsolt Viczian
c6339b28ac 1.2.21 - blockquote transclusion fixed 2021-08-28 16:20:43 +02:00
Zsolt Viczian
f24e4fce9c 1.2.20 wrapText() 2021-08-28 15:56:45 +02:00
Zsolt Viczian
5eff9b2e54 1.2.19 2021-08-28 11:57:09 +02:00
Zsolt Viczian
d89c019612 1.2.18 2021-08-27 19:12:29 +02:00
Zsolt Viczian
01d3c13cce 1.2.17 2021-08-25 20:48:36 +02:00
Zsolt Viczian
de68ebbe7d 1.2.16 2021-08-22 13:05:47 +02:00
Zsolt Viczian
caee4f7500 1.2.16 2021-08-22 12:38:09 +02:00
Zsolt Viczian
6d28546677 1.2.15 - link preview inside Excalidraw 2021-08-21 23:01:12 +02:00
Zsolt Viczian
ec5a13f9e4 1.2.14 2021-08-10 20:57:12 +02:00
Zsolt Viczian
b788118880 1.2.13 2021-08-05 21:47:44 +02:00
Zsolt Viczian
6e22e0a428 1.2.13 draft 2021-08-04 22:50:26 +02:00
zsviczian
8b8b469569 Merge pull request #117 from Quorafind/master
Update zh-cn.ts
2021-08-04 22:30:44 +02:00
Boninall
789851c0c4 Update zh-cn.ts
Add some period and Fix some language error
2021-08-04 22:46:01 +08:00
Zsolt Viczian
816de255ee 1.2.12 zh-cn translation 2021-08-03 20:16:56 +02:00
zsviczian
54d4eb7ab4 Merge pull request #113 from Quorafind/master
Update zh-cn.ts
2021-08-03 19:59:30 +02:00
Boninall
e3242ebfb7 Merge branch 'zsviczian:master' into master 2021-08-03 12:51:58 +08:00
Zsolt Viczian
2e36d83abc 1.2.11 2021-08-02 20:34:56 +02:00
Boninall
6552e6bd7b Update zh-cn.ts
Add zh-cn Language
2021-08-02 18:42:58 +08:00
Zsolt Viczian
21ff1833a8 1.2.10 2021-08-01 19:32:37 +02:00
Zsolt Viczian
1b931abd38 changed to my own Excalidraw package 2021-07-31 19:21:06 +02:00
zsviczian
fe28098776 1.2.9 release 2021-07-23 20:10:44 +02:00
zsviczian
72c158e08b save.settings 2021-07-23 19:13:52 +02:00
Zsolt Viczian
5133ab028b style changes context menu 2021-07-14 21:26:47 +02:00
Zsolt Viczian
5fc0f70ded 1.2.8 2021-07-14 20:47:23 +02:00
Zsolt Viczian
08f489f1c5 1.2.8 autosave safeguard, merged file error 2021-07-14 20:04:08 +02:00
zsviczian
dd476b564a workaround to solve issue #94
trim part after final closing } curly bracket
2021-07-14 11:55:40 +02:00
zsviczian
b7a7c9473e dirty changed to file.path
to ensure the plugin won't accidentally overwrite the next file
2021-07-14 11:29:30 +02:00
Zsolt Viczian
cc7dc16810 1.2.7 autosave+template warning 2021-07-13 22:47:38 +02:00
Zsolt Viczian
c45faef141 1.2.6 2021-07-12 23:07:40 +02:00
Zsolt Viczian
ae31ad0870 1.2.5 fame update for .png and .svg when migrating 2021-07-12 07:03:31 +02:00
Zsolt Viczian
ba88ced2ba 1.2.4 2021-07-11 23:28:46 +02:00
Zsolt Viczian
803fb9e234 1.2.3 2021-07-11 08:42:29 +02:00
Zsolt Viczian
d77249088f 1.2.2 text lock/unlock solved 2021-07-10 23:32:25 +02:00
Zsolt Viczian
e6ad7aa304 Excalidraw 0.9 2021-07-10 20:45:09 +02:00
Zsolt Viczian
fdb71a0d03 JSON into codeblock, hover observer corrected 2021-07-10 17:11:02 +02:00
Zsolt Viczian
90c55211ba 1.2.0 2021-07-10 14:45:00 +02:00
zsviczian
8d2c064ee3 Update README1.2.md 2021-07-10 14:31:03 +02:00
Zsolt Viczian
b8a95392f1 readme 1.2 - draft 2021-07-10 14:20:20 +02:00
zsviczian
5430bc4b38 Update README1.2.md 2021-07-10 13:20:30 +02:00
Zsolt Viczian
c9d12c7295 readme 1.2 2021-07-10 12:26:44 +02:00
Zsolt Viczian
c37cbd7e31 improved hover preivew, experimental file tagging 2021-07-10 11:59:18 +02:00
Zsolt Viczian
fbc342189b getScene() returns JSON object not string 2021-07-09 20:28:06 +02:00
Zsolt Viczian
454c68b4b9 1.2.0-beta-1 2021-07-08 22:59:12 +02:00
Zsolt Viczian
09889d7ed3 1.2.0-alpha-4 2021-07-07 23:03:36 +02:00
Zsolt Viczian
096efc45d7 Added migration modal window to help with conversion 2021-07-06 22:16:51 +02:00
Zsolt Viczian
55291d8c27 two minor errors corrected 2021-07-05 21:30:40 +02:00
Zsolt Viczian
e81787ee4b 1.2.0-alpha-3 2021-07-04 22:37:04 +02:00
Zsolt Viczian
ebcf807501 1.2.0-alpha-2 2021-07-04 12:15:42 +02:00
Zsolt Viczian
bd155eced3 language file cleanup 2021-07-04 06:59:32 +02:00
Zsolt Viczian
2b7d0d5dc2 [[ encoding (incl. migration), template link. 2021-07-03 17:02:40 +02:00
Zsolt Viczian
2af2be2078 2.0 alpha release 2021-07-03 13:41:50 +02:00
Zsolt Viczian
6a2e010925 debugging 2021-07-03 08:15:48 +02:00
Zsolt Viczian
ea1b968d89 getViewData fixed 2021-07-02 23:33:32 +02:00
Zsolt Viczian
d1cf5d8c15 getViewData is not yet working 2021-07-02 22:33:38 +02:00
Zsolt Viczian
fc9088b251 TextElement lock / unlock 2021-07-01 06:23:24 +02:00
Zsolt Viczian
97a9a57685 Testing ExcalidrawData 2021-06-29 23:04:10 +02:00
Zsolt Viczian
47ad2da74b Excalidraw 2.0 very early draft 2021-06-27 23:14:35 +02:00
Zsolt Viczian
3551ce827a moved onLayoutReady to registerEventListenrs 2021-06-26 15:06:54 +02:00
Zsolt Viczian
d126b1ca1c minor cleanup 2021-06-26 14:34:30 +02:00
Zsolt Viczian
169d7b9919 minor refactor 2021-06-26 13:43:17 +02:00
Zsolt Viczian
b26f2f39b8 updated package.json dependencies 2021-06-26 13:27:57 +02:00
Zsolt Viczian
388f6ee92b 1.1.10 2021-06-21 20:41:15 +02:00
zsviczian
6c75f6d69b Update README.md 2021-06-21 09:19:27 +02:00
Zsolt Viczian
5b90ff486f update readme 2021-06-20 20:05:50 +02:00
Zsolt Viczian
da163344af 1.1.9 Readme 2021-06-20 19:59:20 +02:00
Zsolt Viczian
81550b61ce 1.1.9 readme update 2021-06-20 19:58:11 +02:00
Zsolt Viczian
4cf623065a 1.1.9 2021-06-20 19:53:15 +02:00
Zsolt Viczian
7ea7cf5f65 false alarm... 2021-06-06 21:36:18 +02:00
Zsolt Viczian
081f2c0368 1.1.8 2021-06-06 15:29:48 +02:00
Zsolt Viczian
0205847751 link index draft - not working yet 2021-06-05 21:18:33 +02:00
Zsolt Viczian
b796ba12f2 quick-preview-beta 2021-06-05 12:39:24 +02:00
Zsolt Viczian
21c564f59c spelling errors 2021-05-29 06:27:47 +02:00
Zsolt Viczian
5bbe90182d 1.1.7 2021-05-27 21:39:53 +02:00
Zsolt Viczian
6174e45c3f 1.1.6 2021-05-25 22:07:19 +02:00
Zsolt Viczian
caebd71dc8 1.1.5 2021-05-24 15:17:31 +02:00
Zsolt Viczian
740ff8df6f 1.1.4 2021-05-20 22:30:44 +02:00
Zsolt Viczian
2123ec4f48 1.1.3 2021-05-20 18:35:03 +02:00
zsviczian
fe1e75e114 Update README.md 2021-05-20 11:01:35 +02:00
zsviczian
53a9af7a83 Update README.md 2021-05-20 10:59:58 +02:00
zsviczian
750a38a20f Update README.md 2021-05-20 10:58:34 +02:00
Zsolt Viczian
222a23fafc readme styling 2021-05-20 10:57:39 +02:00
Zsolt Viczian
2308343b28 updated readme headings 2021-05-20 10:54:51 +02:00
Zsolt Viczian
1a5a35585f updated readme 2021-05-20 10:51:04 +02:00
Zsolt Viczian
1aa7e66a59 updated readme with style 2021-05-20 10:47:16 +02:00
Zsolt Viczian
c3efb9addc Release 1.1.2: fixed image color 2021-05-20 10:43:38 +02:00
Zsolt Viczian
061b663c12 1.1.1 release 2021-05-20 07:39:46 +02:00
zsviczian
06cb55534b Update README.md 2021-05-19 23:35:58 +02:00
Zsolt Viczian
fbdd419d01 1.1.0 release 2021-05-19 23:30:40 +02:00
Zsolt Viczian
a5b7ee8a06 before removing codeblock processor 2021-05-19 20:09:47 +02:00
Zsolt Viczian
fca6ce83f0 1.1-test 2021-05-18 23:39:50 +02:00
zsviczian
e472dbebb2 Update README.md 2021-05-18 12:33:12 +02:00
Zsolt Viczian
936500eb82 before code mirror change 2021-05-17 20:19:39 +02:00
Zsolt Viczian
af3f86ce15 1.0.12 release 2021-05-12 19:45:24 +02:00
Zsolt Viczian
453be7915d 1.0.11 release 2021-05-11 19:51:46 +02:00
zsviczian
f01c05e501 Update README.md 2021-05-10 20:19:08 +02:00
zsviczian
8e306a7d1f Update README.md 2021-05-10 20:18:19 +02:00
zsviczian
e358032d18 Update README.md 2021-05-10 20:16:48 +02:00
Zsolt Viczian
45c6c4680a 1.0.10 release 2021-05-10 20:15:52 +02:00
Zsolt Viczian
d7cf04cef8 1.0.10-test 2021-05-09 17:36:52 +02:00
zsviczian
0cb4c0c4d2 Update readme.md 2021-05-08 22:15:33 +02:00
zsviczian
f13c5a2df5 Update readme.md 2021-05-08 22:15:12 +02:00
zsviczian
f5e0d56d99 Update readme.md 2021-05-08 22:13:46 +02:00
Zsolt Viczian
7ac04c0f74 ko-fi 2021-05-08 22:12:57 +02:00
zsviczian
934eea794b Update README.md 2021-05-08 20:45:55 +02:00
Zsolt Viczian
045ee288d5 updated navigation 2021-05-08 20:42:11 +02:00
Zsolt Viczian
7455405425 added navigation 2021-05-08 20:33:04 +02:00
zsviczian
9c52a7d851 Set theme jekyll-theme-leap-day 2021-05-08 20:29:49 +02:00
zsviczian
c9a7930a04 Update dataviewjs_familytree.md 2021-05-08 20:27:41 +02:00
zsviczian
fdee55ccf0 Update readme.md 2021-05-08 20:27:23 +02:00
Zsolt Viczian
b4d9469b7d docs v1.0 2021-05-08 20:26:09 +02:00
zsviczian
caecd7422b Update dataviewjs.md 2021-05-08 19:49:26 +02:00
zsviczian
7ed33646e0 Update dataviewjs.md 2021-05-08 19:49:09 +02:00
zsviczian
d491e24605 Update readme.md 2021-05-08 19:44:09 +02:00
zsviczian
65baf16d8a Update readme.md 2021-05-08 19:43:22 +02:00
Zsolt Viczian
5bc756288c Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2021-05-08 19:42:52 +02:00
Zsolt Viczian
dd41cd1eeb . 2021-05-08 19:42:40 +02:00
zsviczian
0e8ff2f5cf Update readme.md 2021-05-08 19:40:16 +02:00
Zsolt Viczian
aa78e7ea54 docs testing 2021-05-08 19:36:50 +02:00
zsviczian
18c0badc25 Rename AutomateHowTo.md to readme.md 2021-05-08 19:31:17 +02:00
zsviczian
34f08766f4 Set theme jekyll-theme-hacker 2021-05-08 19:27:54 +02:00
Zsolt Viczian
8cbc0f1d53 experimenting with docs 2021-05-08 19:25:43 +02:00
Zsolt Viczian
dc8223b6fa . 2021-05-08 09:56:07 +02:00
Zsolt Viczian
b445a62f50 . 2021-05-08 09:10:20 +02:00
Zsolt Viczian
3dcc156e46 . 2021-05-08 09:08:50 +02:00
Zsolt Viczian
ba5c132f17 . 2021-05-08 09:06:53 +02:00
Zsolt Viczian
a6671ff35b updated insert new drawing example 2021-05-08 09:06:33 +02:00
Zsolt Viczian
3a48db940d updated documentation 2021-05-08 07:57:31 +02:00
Zsolt Viczian
524626cb5b updated ExcalidrawAutomate documentation 2021-05-08 07:56:00 +02:00
Zsolt Viczian
bd06d08071 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2021-05-08 07:51:33 +02:00
Zsolt Viczian
76ca98e3ed 1.0.9 minor fix to theme in ExcalidrawAutomate 2021-05-08 07:51:15 +02:00
zsviczian
ce523e5887 Update README.md 2021-05-08 07:46:10 +02:00
zsviczian
e5f94b3fba Update AutomateHowTo.md 2021-05-08 07:26:07 +02:00
Zsolt Viczian
ddb01297a7 1.0.8 release 2021-05-07 20:33:51 +02:00
zsviczian
be4b363cb6 Update README.md 2021-05-07 20:33:30 +02:00
zsviczian
359bb54752 Update AutomateHowTo.md 2021-05-07 19:11:13 +02:00
Zsolt Viczian
d15278e70e tooltip edit 2021-05-06 23:23:59 +02:00
Zsolt Viczian
4be1ff89fe Force-Save tooltip 2021-05-06 23:20:42 +02:00
Zsolt Viczian
c962168c52 solved #46, #45, #44, #41, #40, #38, #37 2021-05-06 23:15:07 +02:00
Zsolt Viczian
660f6e03b1 trying to resolve issue with slidingPanes 2021-05-06 22:44:57 +02:00
Zsolt Viczian
a26e565d04 TransclusionIndex working 2021-05-06 20:56:48 +02:00
Zsolt Viczian
0debaace4e parser 2021-05-06 09:16:16 +02:00
Zsolt Viczian
126086f9f1 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2021-05-06 06:22:14 +02:00
Zsolt Viczian
baf2cdd5d8 save on close 2021-05-06 06:21:42 +02:00
zsviczian
793302a1f5 Update README.md 2021-05-05 22:28:33 +02:00
zsviczian
0d361340c1 Update AutomateHowTo.md 2021-05-05 20:54:00 +02:00
zsviczian
e192da8668 Update AutomateHowTo.md 2021-05-05 20:53:24 +02:00
122 changed files with 19767 additions and 8583 deletions

4
.eslintignore Normal file
View File

@@ -0,0 +1,4 @@
node_modules/
.github/
docs/
images/

7
.eslintrc.json Normal file
View File

@@ -0,0 +1,7 @@
{
"extends": ["@excalidraw/eslint-config"],
"rules": {
"import/no-anonymous-default-export": "off",
"no-restricted-globals": "off"
}
}

View File

@@ -4,8 +4,9 @@ Excalidraw Automate allows you to create Excalidraw drawings using the [Template
With a little work, using Excalidraw Automate you can generate simple mindmaps, fill out SVG forms, create customized charts, etc. based on documents in your vault.
You can access Excalidraw Automate via the ExcalidrawAutomate object. I recommend staring your Automate scripts with the following code.
You can access Excalidraw Automate via the ExcalidrawAutomate object. I recommend starting your Automate scripts with the following code.
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
const ea = ExcalidrawAutomate;
ea.reset();
@@ -22,7 +23,26 @@ The second line resets ExcalidrawAutomate to defaults. This is important as you
You can change styling between adding different elements. My logic for separating element styling and creation is based on the assumption that you will probably set a stroke color, stroke style, stroke roughness, etc. and draw most of your elements using this. There would be no point in setting all these parameters each time you add an element.
### Before we dive deeper, here's a simple example script
### Before we dive deeper, here are two a simple example scripts
#### Create a new drawing with custom name, in a custom folder, using a template
This simple script gives you significant additional flexibility over Excalidraw Plugin settings to name your drawings, place them into folders, and to apply templates.
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const ea = ExcalidrawAutomate;
ea.reset();
await ea.create({
filename : tp.date.now("HH.mm"),
foldername : tp.date.now("YYYY-MM-DD"),
templatePath: "Excalidraw/Template1.excalidraw",
onNewPane : false
});
%>
```
#### Create a simple drawing
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const ea = ExcalidrawAutomate;
@@ -43,7 +63,7 @@ The script will generate the following drawing:
## Attributes and functions at a glance
Here's the interface implemented by ExcalidrawAutomate:
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
ExcalidrawAutomate: {
style: {
@@ -268,7 +288,7 @@ Groups objects listed in `objectIds`.
```typescript
async toClipboard(templatePath?:string)
```
Places the generated drawing to the clipboard. Useful when you don't want to create a new drawing, but want to paste additional items onto an exising drawing.
Places the generated drawing to the clipboard. Useful when you don't want to create a new drawing, but want to paste additional items onto an existing drawing.
### create()
```typescript
@@ -301,7 +321,31 @@ async createPNG(templatePath?:string)
Returns a blob containing a PNG image of the generated drawing.
## Examples
### Insert new drawing into currently edited document
This template will prompt you for the title of the drawing. It will create a new drawing with the provided title, and in the folder of the document you were editing. It will then transclude the new drawing at the cursor location and open the new drawing in a new workspace leaf by splitting the current leaf.
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const defaultTitle = tp.date.now("HHmm")+' '+tp.file.title;
const title = await tp.system.prompt("Title of the drawing?", defaultTitle);
const folder = tp.file.folder(true);
const transcludePath = (folder== '/' ? '' : folder + '/') + title + '.excalidraw';
tR = String.fromCharCode(96,96,96)+'excalidraw\n[['+transcludePath+']]\n'+String.fromCharCode(96,96,96);
const ea = ExcalidrawAutomate;
ea.reset();
ea.setTheme(1); //set Theme to dark
await ea.create({
filename : title,
foldername : folder,
//templatePath: 'Excalidraw/Template.excalidraw', //uncomment if you want to use a template
onNewPane : true
});
%>
```
### Connect objects
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const ea = ExcalidrawAutomate;
@@ -317,6 +361,8 @@ Returns a blob containing a PNG image of the generated drawing.
```
### Using a template
This example is similar to the first one, but rotated 90°, and using a template, plus specifying a filename and folder to save the drawing, and opening the new drawing in a new pane.
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const ea = ExcalidrawAutomate;
@@ -337,7 +383,7 @@ This example is similar to the first one, but rotated 90°, and using a template
### Generating a simple mindmap from a text outline
This is a slightly more elaborate example. This will generate an a mindmap from a tabulated outline.
![Drawing 2021-05-05 20 14 04](https://user-images.githubusercontent.com/14358394/117189615-afe07580-adde-11eb-81da-fa4bd84d4970.png)
![Drawing 2021-05-05 20 52 34](https://user-images.githubusercontent.com/14358394/117194124-00a69d00-ade4-11eb-8b75-5e18a9cbc3cd.png)
Example input:
```
@@ -355,6 +401,8 @@ Example input:
```
The script:
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const IDX = Object.freeze({"depth":0, "text":1, "parent":2, "size":3, "children": 4, "objectId":5});
@@ -414,13 +462,13 @@ offsets = [0];
for(i=0;i<=linecount;i++) {
depth = tree[i][IDX.depth];
if (depth == 1) ea.style.strokeColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16);
tree[i][IDX.objectId] = ea.addText(depth*width,((tree[i][IDX.size]/2)+offsets[depth])*height,tree[i][IDX.text],{startArrowHead: 'dot',box:true});
tree[i][IDX.objectId] = ea.addText(depth*width,((tree[i][IDX.size]/2)+offsets[depth])*height,tree[i][IDX.text],{box:true});
//set child offset equal to parent offset
if((depth+1)>offsets.length) offsets.push(offsets[depth]);
else offsets[depth+1] = offsets[depth];
offsets[depth] += tree[i][IDX.size];
if(tree[i][IDX.parent]!=-1) {
ea.connectObjects(tree[tree[i][IDX.parent]][IDX.objectId],"right",tree[i][IDX.objectId],"left",);
ea.connectObjects(tree[tree[i][IDX.parent]][IDX.objectId],"right",tree[i][IDX.objectId],"left",{startArrowHead: 'dot'});
}
}

169
README.md
View File

@@ -1,99 +1,96 @@
# Obsidian Excalidraw Plugin
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 and you can transclude drawings into your documents. For a showcase of Excalidraw features, please read my blog post [here](https://www.zsolt.blog/2021/03/showcasing-excalidraw.html).
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.
**See details of the 1.0.6 and 1.0.7 release including a short video, further below**
Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
![image](https://user-images.githubusercontent.com/14358394/115983515-d06c2c80-a5a1-11eb-8d12-c7df91d18107.png)
![image](https://user-images.githubusercontent.com/14358394/125159831-336d6880-e17a-11eb-8a3d-ceabc2555a08.png)
## Key features
- The plugin adds the following actions to the **command palette**:
- To create a new drawing
- To find and edit existing drawings in your vault,
- To embed (transclude) a drawing into a document, and
- To export a drawing as PNG or SVG.
- You can also use the **file explorer** in your vault to open Excalidraw files.
- Use the **ribbon button** to create a new drawing, do CTRL+Click to open on a new page.
- Open settings to set up a **default folder** for new drawings.
- Set up a **Template** by creating a drawing, customizing it the way you like it, and specifying the file as the template in settings.
- The plugin saves drawings to your vault as a file with the *.excalidraw* file extension.
- You can customize the **size and position of the embedded image** using the `[[image.excalidraw|100]]`, `[[image.excalidraw|100x100]]`, `[[image.excalidraw|100|left]]`, `[[image.excalidraw|right-wrap]]`, formatting options. `[[<filename.excalidraw>|<width>x<height>|<alignment>]]`. You can add your custom alignment via css. Any text that appears in `<alignment>` will be added as style to the SVG element and the wrapper DIV element. Check below and styles.css for more insight.
- You can setup Excalidraw to **automatically export SVG and/or PNG** files for your drawings, and to keep those in sync with your drawing.
## How to?
Part 1: Intro to Obsidian-Excalidraw - Start a new drawing (3:12)
[![Part 1: Intro to Obsidian-Excalidraw - Start a new drawing](https://user-images.githubusercontent.com/14358394/115983840-05797e80-a5a4-11eb-93cd-bae4b1973f72.jpg)](https://youtu.be/i-hIfY-Ecjg)
Part 2: Intro to Obsidian-Excalidraw - Basic features (6:06)
[![Part 2: Intro to Obsidian-Excalidraw - Basic features](https://user-images.githubusercontent.com/14358394/115983902-699c4280-a5a4-11eb-973d-2ba1bd7ac2db.jpg)](https://youtu.be/-dk7pvdl-H0)
Part 3: Intro to Obsidian-Excalidraw - Advanced features (3:26)
[![Part 3: Intro to Obsidian-Excalidraw - Advanced features](https://user-images.githubusercontent.com/14358394/115983916-7de03f80-a5a4-11eb-8f36-4ad516ef9e80.jpg)](https://youtu.be/2cKlEwo8WU0)
Part 4: Intro to Obsidian-Excalidraw - Setting up a template (1:45)
[![Part 4: Intro to Obsidian-Excalidraw - Setting up a template](https://user-images.githubusercontent.com/14358394/115983929-92bcd300-a5a4-11eb-9d4f-03e5cb9e3ebf.jpg)](https://youtu.be/oNPYZEpmuJ8)
Part 5: Intro to Obsidian-Excalidraw - Stencil Library (3:16)
[![Part 5: Intro to Obsidian-Excalidraw - Stencil Library](https://user-images.githubusercontent.com/14358394/115983944-a8ca9380-a5a4-11eb-8a69-e74ae00d95be.jpg)](https://youtu.be/rLx-9FvlzgI)
Part 6: Intro to Obsidian-Excalidraw: Embedding drawings (2:08)
[![Part 6: Intro to Obsidian-Excalidraw: Embedding drawings](https://user-images.githubusercontent.com/14358394/115983954-bbdd6380-a5a4-11eb-9243-f0151451afcd.jpg)](https://youtu.be/JQeJ-Hh-xAI)
## 1.0.6 and 1.0.7 update
[![1.0.6 Update](https://user-images.githubusercontent.com/14358394/116312909-58725200-a7ad-11eb-89b9-c67cb48ffebb.jpg)](https://youtu.be/ipZPbcP2B0M)
### SVG styling when embedding using a code block
- 1.0.7 adds further flexibility to styling
- new formatting option for the code block embedding
- Valid values: `left`, `right`, `left-wrap`, `right-wrap`... but anything after the last `|` character will be added to the class of the SVG element and the wrapper DIV element.
Here is the corresponding CSS:
```
svg.excalidraw-svg-right-wrap {
float: right;
margin: 0px 0px 20px 20px;
}
svg.excalidraw-svg-left-wrap {
float: left;
margin: 0px 35px 20px 0px;
}
div.excalidraw-svg-right {
text-align: right;
}
div.excalidraw-svg-left {
text-align: left;
}
```
# Video walkthrough
| | | |
|----|----|----|
|[![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)|
## Known issues
- On mobile (iOS and Android): As you draw left to right it opens left sidebar. Draw right to left, opens right sidebar. Draw down, opens commands palette. So seems open is emulating the gestures, even when drawing towards the center. I understand that the issue will be resolved in the next release of Obsidian mobile.
- I have seen two cases when adding a stencil library did not work. In both cases, the end solution was a reinstall of Obsidian. The root cause is not clear, but maybe because of the incremental updates of Obsidian from an early version.
- Sync does not support .excalidraw files. This issue will be addressed in a later release of Obsidian sync. Until then, here are two hacks you can play with:
- You have the option to use OneDrive, Google Drive, iCloud, DropBox, etc. to sync your vault between devices.
- You can also use Obsidian Sync in conjunction with "Obsidian Git" (find it in community plugins). Be sure to set up git to ignore all files except for .excalidraw by adding the following to `.gitignore`. Obsidian Git does not work on mobile, but on Android you can use an app like MGIT to sync your `.excalidraw` files from/to the git repository.
```
#ignore all kind of files
*.*
#except excalidraw files
!*.excalidraw
```
## Tips and tricks
- If you want to sketch in fullscreen, I recommend installing the [Fullscreen Focus Mode](https://github.com/razumihin/obsidian-fullscreen-plugin) plugin.
# Key features
- The plugin aims to integrate Excalidraw seamlessly into Obsidian including Command Palette actions, File Explorer features, Option Menu commands, and the Ribbon Button.
- CTRL/CMD+Click on the ribbon button, or in the file explorer to create / open drawings in a new pane.
- Settings will allow you to customize Excalidraw to your needs:
- Default folder for new drawings and define custom filename pattern for new drawings.
- Template for new drawings. The template will restore stroke properties. This means you can set up defaults in your template for stroke color, stroke width, opacity, font family, font size, fill style, stroke style, etc. This also applies to ExcalidrawAutomate.
- If portability is important to you: Auto-export SVG and/or PNG files including keep-in-sync feature so you can embed SVG/PNG into your documents instead of embedding excalidraw files.
- 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. Any text that appears in `<alignment>` will be added to the rendered SVG element style and to the wrapper DIV element. Check below and styles.css for more insight.
- Supports hyperlinks e.g. `https://zsolt.blog`, `[Obsidian](https://obsidian.md)`, and internal links e.g. `[[My file in vault|Alias]]` in drawing text.
- Links will update when files are moved or renamed, if you have the Obsidian setting Files & Links/Automatically Update Internal Links enabled.
- Links in drawings will show up in backlinks of documents
- 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
- CTRL/CMD + hover to bring up the Obsidian quick preview for the link. (On Mac it is CTRL+CMD+hover).
- CTRL/CMD + CLICK a text element to open it as a link.
- CTRL/CMD + ALT + CLICK to create the file (if it does not yet exist) and open it
- CTRL/CMD + SHIFT + CLICK to open the file in a new pane
- CTRL/CMD + ALT + SHIFT + CLICK to create the file (if it does not yet exist) and open it in a new pane
- Using the block reference you can also reference & transclude text that appears on drawings, in other documents
- Insert LaTeX formulas using the Command Palette action "Insert LaTeX formula". You can edit formulas either in Markdown view, or by CTRL/CMD + Click on the formula.
- Drag & Drop support
- You can drag files from the Obsidian file explorer and they will become links to those files in Excalidraw.
- Dragging image files (PNG, SVG, JPG, Excalidraw) from obsidian files explorer while pressing the CTRL/CMD button will embed the image into your drawing.
- You can drag and drop images from outside obsidian onto Excalidraw. These images will be embedded into your drawing and saved to Obsidian.
- 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.
- 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
- Embed complete markdown files into your drawings
- Drag from the desired file from the Obsidian file explorer and hold down CTRL/CMD while dropping the file onto the canvas.
- Use the command palette action: `Insert markdown file from vault`
- Use custom woff, woff2 or TTF font to display the document, you can set the default font to use under Excalidraw Settings.
- You can set a custom css for rendering the snapshot image of your markdown document. Only operating system standard fonts are supported as the font-family ([Win10](https://docs.microsoft.com/en-us/typography/fonts/windows_10_font_list), [Mac & iOS](https://developer.apple.com/fonts/system-fonts/)), plus you can set one additional custom font using the setting explained above. (for a demonstration watch this [video](https://youtu.be/K6qZkTz8GHs) and check out this [sample css](https://github.com/zsviczian/obsidian-excalidraw-plugin/discussions/281)).
- To help with styling you can observe the SVG snapshot of the markdown document created by Excalidraw. Open Obsidian Developer Console (CTRL+Shift+i) and execute the following command: `ExcalidrawAutomate.mostRecentMarkdownSVG`
- 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-css: "css-filename|css snippet"`
- Switch to markdown view or use CTRL/CMD+ALT/OPT click on the image to edit properties of the embed: `[[filename#^blockref|WIDTHxMAXHEIGHT]]`
- 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
# Known issues
- Mobile support
- Partially mitigated in 1.0.10 by the introduction of autosave: Your drawing will not be saved when you terminate the mobile app by closing the Obsidian task.
- Text elements "jumps off screen" when editing, if drawing is zoomed in and text element does not fit the visible screen area. I am working on a resolution.
# Tips and tricks
- [Ozan's Image in Editor Plugin](https://github.com/ozntel/oz-image-in-editor-obsidian). In a nice collaboration with Ozan, his Image-in-Editor plugin now supports Excalidraw. I recommend installing his plugin to display drawings also in Edit mode.
# Feedback, questions, ideas, problems
Join the conversation about the Excalidraw plugin on [forum.obsidian.md](https://forum.obsidian.md/t/excalidraw-full-featured-sketching-plugin-in-obsidian)
Please head over to [GitHub](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues) to report a bug or request an enhancement.
## Say Thank You
# 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.

14
TODO.md Normal file
View File

@@ -0,0 +1,14 @@
[x] do not embed font into SVG when embedding Excalidraw into other Excalidraw
[x] add ```html <SVG>...</SVG> ``` codeblock to excalidraw markdown
[x] read pre-saved `<SVG>` when generating image preview
[x] update code to adopt change files moving from AppState to App
- Add "files" to legacy excalidraw export
[x] PNG preview
[x] markdown embed SVG 190
[x] markdown embed PNG
[x] embed Excalidraw into other Excalidraw

View File

@@ -0,0 +1,186 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Attributes and functions overview
Here's the interface implemented by ExcalidrawAutomate:
```typescript
export interface ExcalidrawAutomate {
plugin: ExcalidrawPlugin;
elementsDict: {}; //contains the ExcalidrawElements currently edited in Automate indexed by el.id
imagesDict: {}; //the images files including DataURL, indexed by fileId
style: {
strokeColor: string; //https://www.w3schools.com/colors/default.asp
backgroundColor: string;
angle: number; //radian
fillStyle: FillStyle; //type FillStyle = "hachure" | "cross-hatch" | "solid"
strokeWidth: number;
strokeStyle: StrokeStyle; //type StrokeStyle = "solid" | "dashed" | "dotted"
roughness: number;
opacity: number;
strokeSharpness: StrokeSharpness; //type StrokeSharpness = "round" | "sharp"
fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia
fontSize: number;
textAlign: string; //"left"|"right"|"center"
verticalAlign: string; //"top"|"bottom"|"middle" :for future use, has no effect currently
startArrowHead: string; //"triangle"|"dot"|"arrow"|"bar"|null
endArrowHead: string;
};
canvas: {
theme: string; //"dark"|"light"
viewBackgroundColor: string;
gridSize: number;
};
setFillStyle(val: number): void; //0:"hachure", 1:"cross-hatch" 2:"solid"
setStrokeStyle(val: number): void; //0:"solid", 1:"dashed", 2:"dotted"
setStrokeSharpness(val: number): void; //0:"round", 1:"sharp"
setFontFamily(val: number): void; //1: Virgil, 2:Helvetica, 3:Cascadia
setTheme(val: number): void; //0:"light", 1:"dark"
addToGroup(objectIds: []): string;
toClipboard(templatePath?: string): void;
getElements(): ExcalidrawElement[]; //get all elements from ExcalidrawAutomate elementsDict
getElement(id: string): ExcalidrawElement; //get single element from ExcalidrawAutomate elementsDict
create(params?: {
//create a drawing and save it to filename
filename?: string; //if null: default filename as defined in Excalidraw settings
foldername?: string; //if null: default folder as defined in Excalidraw settings
templatePath?: string;
onNewPane?: boolean;
frontmatterKeys?: {
"excalidraw-plugin"?: "raw" | "parsed";
"excalidraw-link-prefix"?: string;
"excalidraw-link-brackets"?: boolean;
"excalidraw-url-prefix"?: string;
};
}): Promise<string>;
createSVG(
templatePath?: string,
embedFont?: boolean,
exportSettings?: ExportSettings, //use ExcalidrawAutomate.getExportSettings(boolean,boolean)
loader?: EmbeddedFilesLoader, //use ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?)
theme?: string,
): Promise<SVGSVGElement>;
createPNG(
templatePath?: string,
scale?: number,
exportSettings?: ExportSettings, //use ExcalidrawAutomate.getExportSettings(boolean,boolean)
loader?: EmbeddedFilesLoader, //use ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?)
theme?: string,
): Promise<any>;
wrapText(text: string, lineLen: number): string;
addRect(topX: number, topY: number, width: number, height: number): string;
addDiamond(topX: number, topY: number, width: number, height: number): string;
addEllipse(topX: number, topY: number, width: number, height: number): string;
addBlob(topX: number, topY: number, width: number, height: number): string;
addText(
topX: number,
topY: number,
text: string,
formatting?: {
wrapAt?: number;
width?: number;
height?: number;
textAlign?: string;
box?: boolean | "box" | "blob" | "ellipse" | "diamond"; //if !null, text will be boxed
boxPadding?: number;
},
id?: string,
): string;
addLine(points: [[x: number, y: number]]): string;
addArrow(
points: [[x: number, y: number]],
formatting?: {
startArrowHead?: string;
endArrowHead?: string;
startObjectId?: string;
endObjectId?: string;
},
): string;
addImage(topX: number, topY: number, imageFile: TFile): Promise<string>;
addLaTex(topX: number, topY: number, tex: string): Promise<string>;
connectObjects(
objectA: string,
connectionA: ConnectionPoint, //type ConnectionPoint = "top" | "bottom" | "left" | "right" | null
objectB: string,
connectionB: ConnectionPoint, //when passed null, Excalidraw will automatically decide
formatting?: {
numberOfPoints?: number; //points on the line. Default is 0 ie. line will only have a start and end point
startArrowHead?: string; //"triangle"|"dot"|"arrow"|"bar"|null
endArrowHead?: string; //"triangle"|"dot"|"arrow"|"bar"|null
padding?: number;
},
): void;
clear(): void; //clear elementsDict and imagesDict only
reset(): void; //clear() + reset all style values to default
isExcalidrawFile(f: TFile): boolean; //returns true if MD file is an Excalidraw file
//view manipulation
targetView: ExcalidrawView; //the view currently edited
setView(view: ExcalidrawView | "first" | "active"): ExcalidrawView;
getExcalidrawAPI(): any; //https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw#ref
getViewElements(): ExcalidrawElement[]; //get elements in View
deleteViewElements(el: ExcalidrawElement[]): boolean;
getViewSelectedElement(): ExcalidrawElement; //get the selected element in the view, if more are selected, get the first
getViewSelectedElements(): ExcalidrawElement[];
getViewFileForImageElement(el: ExcalidrawElement): TFile | null; //Returns the TFile file handle for the image element
copyViewElementsToEAforEditing(elements: ExcalidrawElement[]): void; //copies elements from view to elementsDict for editing
viewToggleFullScreen(forceViewMode?: boolean): void;
connectObjectWithViewSelectedElement( //connect an object to the selected element in the view
objectA: string, //see connectObjects
connectionA: ConnectionPoint,
connectionB: ConnectionPoint,
formatting?: {
numberOfPoints?: number;
startArrowHead?: string;
endArrowHead?: string;
padding?: number;
},
): boolean;
addElementsToView( //Adds elements from elementsDict to the current view
repositionToCursor: boolean,
save: boolean,
): Promise<boolean>;
onDropHook(data: {
//if set Excalidraw will call this function onDrop events
ea: ExcalidrawAutomate;
event: React.DragEvent<HTMLDivElement>;
draggable: any; //Obsidian draggable object
type: "file" | "text" | "unknown";
payload: {
files: TFile[]; //TFile[] array of dropped files
text: string; //string
};
excalidrawFile: TFile; //the file receiving the drop event
view: ExcalidrawView; //the excalidraw view receiving the drop
pointerPosition: { x: number; y: number }; //the pointer position on canvas at the time of drop
}): boolean; //a return of true will stop the default onDrop processing in Excalidraw
mostRecentMarkdownSVG: SVGSVGElement; //Markdown renderer will drop a copy of the most recent SVG here for debugging purposes
getEmbeddedFilesLoader(isDark?: boolean): EmbeddedFilesLoader; //utility function to generate EmbeddedFilesLoader object
getExportSettings( //utility function to generate ExportSettings object
withBackground: boolean,
withTheme: boolean,
): ExportSettings;
getBoundingBox(elements: ExcalidrawElement[]): {
//get bounding box of elements
topX: number; //bounding box is the box encapsulating all of the elements completely
topY: number;
width: number;
height: number;
};
//elements grouped by the highest level groups
getMaximumGroups(elements: ExcalidrawElement[]): ExcalidrawElement[][];
//gets the largest element from a group. useful when a text element is grouped with a box, and you want to connect an arrow to the box
getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;
// Returns 2 or 0 intersection points between line going through `a` and `b`
// and the `element`, in ascending order of distance from `a`.
intersectElementWithLine(
element: ExcalidrawBindableElement,
a: readonly [number, number],
b: readonly [number, number],
gap?: number, //if given, element is inflated by this value
): Point[];
//See OCR plugin for example on how to use scriptSettings
activeScript: string; //Set automatically by the ScriptEngine
getScriptSettings(): {}; //Returns script settings. Saves settings in plugin settings, under the activeScript key
setScriptSettings(settings:any):Promise<void>; //sets script settings.
}
```

18
docs/API/canvas_style.md Normal file
View File

@@ -0,0 +1,18 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Canvas style settings
Sets the properties of the canvas.
### theme, setTheme()
String. Valid values are "light" and "dark".
`setTheme()` accepts a number:
- 0: "light"
- any other number: "dark"
### viewBackgroundColor
String. This is the fill color of an object. [CSS Legal Color Values](https://www.w3schools.com/cssref/css_colors_legal.asp)
Allowed values are [HTML color names](https://www.w3schools.com/colors/colors_names.asp), hexadecimal RGB strings e.g. `#FF0000` for red, or `transparent`.
### gridSize
Number. The size of the grid. If set to zero, no grid is displayed.

91
docs/API/element_style.md Normal file
View File

@@ -0,0 +1,91 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Element style settings
As you will notice, some styles have setter functions. This is to help you navigate the allowed values for the property. You do not need to use the setter function however, you can use set the value directly as well.
### strokeColor
String. The color of the line. [CSS Legal Color Values](https://www.w3schools.com/cssref/css_colors_legal.asp)
Allowed values are [HTML color names](https://www.w3schools.com/colors/colors_names.asp), hexadecimal RGB strings, or e.g. `#FF0000` for red.
### backgroundColor
String. This is the fill color of an object. [CSS Legal Color Values](https://www.w3schools.com/cssref/css_colors_legal.asp)
Allowed values are [HTML color names](https://www.w3schools.com/colors/colors_names.asp), hexadecimal RGB strings e.g. `#FF0000` for red, or `transparent`.
### angle
Number. Rotation in radian. 90° == `Math.PI/2`.
### fillStyle, setFillStyle()
```typescript
type FillStyle = "hachure" | "cross-hatch" | "solid";
setFillStyle (val:number);
```
fillStyle is a string.
`setFillStyle()` accepts a number:
- 0: "hachure"
- 1: "cross-hatch"
- any other number: "solid"
### strokeWidth
Number, sets the width of the stroke.
### strokeStyle, setStrokeStyle()
```typescript
type StrokeStyle = "solid" | "dashed" | "dotted";
setStrokeStyle (val:number);
```
strokeStyle is a string.
`setStrokeStyle()` accepts a number:
- 0: "solid"
- 1: "dashed"
- any other number: "dotted"
### roughness
Number. Called sloppiness in Excalidraw. Three values are accepted:
- 0: Architect
- 1: Artist
- 2: Cartoonist
### opacity
Number between 0 and 100. The opacity of an object, both stroke and fill.
### strokeSharpness, setStrokeSharpness()
```typescript
type StrokeSharpness = "round" | "sharp";
setStrokeSharpness(val:nmuber);
```
strokeSharpness is a string.
"round" lines are curvey, "sharp" lines break at the turning point.
`setStrokeSharpness()` accepts a number:
- 0: "round"
- any other number: "sharp"
### fontFamily, setFontFamily()
Number. Valid values are 1,2 and 3.
`setFontFamily()` will also accept a number and return the name of the font.
- 1: "Virgil, Segoe UI Emoji"
- 2: "Helvetica, Segoe UI Emoji"
- 3: "Cascadia, Segoe UI Emoji"
### fontSize
Number. Default value is 20 px
### textAlign
String. Alignment of the text horizontally. Valid values are "left", "center", "right".
This is relevant when setting a fix width using the `addText()` function.
### verticalAlign
String. Alignment of the text vertically. Valid values are "top" and "middle".
This is relevant when setting a fix height using the `addText()` function.
### startArrowHead, endArrowHead
String. Valid values are "arrow", "bar", "dot", and "none". Specifies the beginning and ending of an arrow.
This is relavant when using the `addArrow()` and the `connectObjects()` functions.

102
docs/API/introduction.md Normal file
View File

@@ -0,0 +1,102 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Introduction to the API
You can access Excalidraw Automate via the ExcalidrawAutomate object. I recommend starting Templater, DataView and QuickAdd scripts with the following code:
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
const ea = ExcalidrawAutomate;
ea.reset();
```
The first line creates a constant so you can avoid writing ExcalidrawAutomate 100x times.
The second line resets ExcalidrawAutomate to defaults. This is important as you will not know which template you executed before, thus you won't know what state you left Excalidraw in.
**⚠ Note:** In case you are using the Excalidraw plugin's built in [Scripting Engine](../ExcalidrawScriptsEngine.md), the engine will take care of initializing the `ea` object.
### Basic logic of using Excalidraw Automate
1. Set the styling of the elements you want to draw
2. Add elements. As you add elements, each new element is added one layer above the previous, thus in case of overlapping objects the later one will be on the top of the prior one.
3. Call `await ea.create();` to instantiate the drawing, or use `ea.setView();` followed by `ea.addElementsToView();` to add your elements to an existing view, or create a PNG or SVG image out of your elements using `await ea.createSVG();` or `await ea.createPNG();`;
You can change the styling between adding different elements. My logic for separating element styling and creation is based on the assumption that you will probably set a stroke color, stroke style, stroke roughness, etc. and draw most of your elements using that. There would be no point in setting all these parameters each time you add an element.
### Before we dive deeper, here are three a simple example [Templater](https://github.com/SilentVoid13/Templater) scripts
#### Create a new drawing with custom name, in a custom folder, using a template
This simple script gives you significant additional flexibility over Excalidraw Plugin settings to name your drawings, place them into folders, and to apply templates.
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const ea = ExcalidrawAutomate;
ea.reset();
await ea.create({
filename : tp.date.now("HH.mm"),
foldername : tp.date.now("YYYY-MM-DD"),
templatePath: "Excalidraw/Template1.excalidraw",
onNewPane : false
});
%>
```
#### Create a simple drawing
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const ea = ExcalidrawAutomate;
ea.reset();
ea.addRect(-150,-50,450,300);
ea.addText(-100,70,"Left to right");
ea.addArrow([[-100,100],[100,100]]);
ea.style.strokeColor = "red";
ea.addText(100,-30,"top to bottom",{width:200,textAligh:"center"});
ea.addArrow([[200,0],[200,200]]);
await ea.create();
%>
```
The script will generate the following drawing:
![FristDemo](https://user-images.githubusercontent.com/14358394/116825643-6e5a8b00-ab90-11eb-9e3a-37c524620d0d.png)
#### Add a TextElement in a box to an open Excalidraw View.
Position the new element under the currently selected element, with an arrow from the selected element to the added text.
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const ea = ExcalidrawAutomate;
ea.reset();
ea.setView("first");
selectedElement = ea.getViewSelectedElement();
ea.setStrokeSharpness(0);
const boxPadding = 5;
id = ea.addText(
selectedElement.x + boxPadding,
selectedElement.y+selectedElement.height+100,
"[[Next process step]]",
{
textAlign:"center",
box:true,
boxPadding:boxPadding,
width:selectedElement.width-boxPadding*2,
}
);
ea.setStrokeSharpness(1);
ea.style.roughness= 0;
ea.connectObjectWithViewSelectedElement(
id,
"top",
"bottom",
{
numberOfPoints:2,
startArrowHead:"arrow",
endArrowHead:"dot",
padding:5
});
ea.addElementsToView();
%>
```
[Click here to view animation](https://user-images.githubusercontent.com/14358394/131967188-2a488e38-f742-49d9-ae98-33238a8d4712.mp4)

82
docs/API/objects.md Normal file
View File

@@ -0,0 +1,82 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Adding objects
These functions will add objects to your drawing. The canvas is infinite, and it accepts negative and positive X and Y values. X values increase left to right, Y values increase top to bottom.
![coordinates](https://user-images.githubusercontent.com/14358394/116825632-6569b980-ab90-11eb-827b-ada598e91e46.png)
### addRect(), addDiamond(), addEllipse()
```typescript
addRect(topX:number, topY:number, width:number, height:number):string
addDiamond(topX:number, topY:number, width:number, height:number):string
addEllipse(topX:number, topY:number, width:number, height:number):string
```
Returns the `id` of the object. The `id` is required when connecting objects with lines. See later.
### addText()
```typescript
addText(
topX:number,
topY:number,
text:string,
formatting?:{
wrapAt?:number,
width?:number,
height?:number,
textAlign?:string,
box?: "box"|"blob"|"ellipse"|"diamond",
boxPadding?:number
},
id?:string
):string
```
Adds text to the drawing.
Formatting parameters are optional:
- If `width` and `height` are not specified, the function will calculate the width and height based on the fontFamily, the fontSize and the text provided.
- In case you want to position a text in the center compared to other elements on the drawing, you can provide a fixed height and width, and you can also specify `textAlign` and `verticalAlign` as described above. e.g.: `{width:500, textAlign:"center"}`
- If you want to add a box around the text, set `{box:"box"|"blob"|"ellipse"|"diamond"}`
Returns the `id` of the object. The `id` is required when connecting objects with lines. See later. If `{box:}` then returns the id of the enclosing box object.
### addLine()
```typescript
addLine(points: [[x:number,y:number]]):string
```
Adds a line following the points provided. Must include at least two points `points.length >= 2`. If more than 2 points are provided the interim points will be added as breakpoints. The line will break with angles if `strokeSharpness` is set to "sharp" and will be curvey if it is set to "round".
Returns the `id` of the object.
### addArrow()
```typescript
addArrow(points: [[x:number,y:number]],formatting?:{startArrowHead?:string,endArrowHead?:string,startObjectId?:string,endObjectId?:string}):string ;
```
Adds an arrow following the points provided. Must include at least two points `points.length >= 2`. If more than 2 points are provided the interim points will be added as breakpoints. The line will break with angles if element `style.strokeSharpness` is set to "sharp" and will be curvey if it is set to "round".
`startArrowHead` and `endArrowHead` specify the type of arrow head to use, as described above. Valid values are "none", "arrow", "dot", and "bar". e.g. `{startArrowHead: "dot", endArrowHead: "arrow"}`
`startObjectId` and `endObjectId` are the object id's of connected objects. I recommend using `connectObjects` instead calling addArrow() for the purpose of connecting objects.
Returns the `id` of the object.
### connectObjects()
```typescript
declare type ConnectionPoint = "top"|"bottom"|"left"|"right";
connectObjects(objectA: string, connectionA: ConnectionPoint, objectB: string, connectionB: ConnectionPoint, formatting?:{numberOfPoints: number,startArrowHead:string,endArrowHead:string, padding: number}):void
```
Connects two objects with an arrow. Will do nothing if either of the two elements is of type `line`, `arrow`, or `freedraw`.
`objectA` and `objectB` are strings. These are the ids of the objects to connect. These IDs are returned by addRect(), addDiamond(), addEllipse() and addText() when creating those objects.
`connectionA` and `connectionB` specify where to connect on the object. Valid values are: "top", "bottom", "left", and "right".
`numberOfPoints` set the number of interim break points for the line. Default value is zero, meaning there will be no breakpoint in between the start and the end points of the arrow. When moving objects on the drawing, these breakpoints will influence how the line is rerouted by Excalidraw.
`startArrowHead` and `endArrowHead` work as described for `addArrow()` above.
### addToGroup()
```typescript
addToGroup(objectIds:[]):string
```
Groups objects listed in `objectIds`. Returns the `id` of the group.

221
docs/API/utility.md Normal file
View File

@@ -0,0 +1,221 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Utility functions
### isExcalidrawFile()
```typescript
isExcalidrawFile(f:TFile): boolean
```
Returns true if the file provided is a valid Excalidraw file (either a legacy `*.excalidraw` file or a markdown file with the excalidraw key in the front-matter).
### clear()
`clear()` will clear objects from cache, but will retain element style settings.
### reset()
`reset()` will first call `clear()` and then reset element style to defaults.
### toClipboard()
```typescript
async toClipboard(templatePath?:string)
```
Places the generated drawing to the clipboard. Useful when you don't want to create a new drawing, but want to paste additional items onto an existing drawing.
### getElements()
```typescript
getElements():ExcalidrawElement[];
```
Returns the elements in ExcalidrawAutomate as an array of ExcalidrawElements. This format is usefull when working with ExcalidrawRef.
### getElement()
```typescript
getElement(id:string):ExcalidrawElement;
```
Returns the element object matching the id. If the element does not exist, returns null.
### create()
```typescript
async create(params?:{filename: string, foldername:string, templatePath:string, onNewPane: boolean})
```
Creates the drawing and opens it. Returns the full filepath of the created file.
`filename` is the filename without extension of the drawing to be created. If `null`, then Excalidraw will generate a filename.
`foldername` is the folder where the file should be created. If `null` then the default folder for new drawings will be used according to Excalidraw settings.
`templatePath` the filename including full path and extension for a template file to use. This template file will be added as the base layer, all additional objects added via ExcalidrawAutomate will appear on top of elements in the template. If `null` then no template will be used, i.e. an empty white drawing will be the base for adding objects.
`onNewPane` defines where the new drawing should be created. `false` will open the drawing on the current active leaf. `true` will open the drawing by vertically splitting the current leaf.
`frontmatterKeys` are the set of frontmatter keys to apply to the document
{
excalidraw-plugin?: "raw"|"parsed",
excalidraw-link-prefix?: string,
excalidraw-link-brackets?: boolean,
excalidraw-url-prefix?: string
}
Example:
```javascript
create (
{
filename:"my drawing",
foldername:"myfolder/subfolder/",
templatePath: "Excalidraw/template.excalidraw",
onNewPane: true,
frontmatterKeys: {
"excalidraw-plugin": "parsed",
"excalidraw-link-prefix": "",
"excalidraw-link-brackets": true,
"excalidraw-url-prefix": "🌐",
}
}
);
```
### createSVG()
```typescript
async createSVG(templatePath?:string)
```
Returns an HTML SVGSVGElement containing the generated drawing.
### createPNG()
```typescript
async createPNG(templatePath?:string, scale:number=1)
```
Returns a blob containing a PNG image of the generated drawing.
### wrapText()
```typescript
wrapText(text:string, lineLen:number):string
```
Returns a string wrapped to the provided max lineLen.
### Accessing the open Excalidraw view
You first need to initialize targetView, before using any of the view manipulation functions.
#### targetView
```typescript
targetView: ExcalidrawView
```
The open Excalidraw View configured as the target of the view operations. User `setView` to initialize.
#### setView()
```typescript
setView(view:ExcalidrawView|"first"|"active"):ExcalidrawView
```
Setting the ExcalidrawView that will be the target of the View operations. Valid `view` input values are:
- an object instance of ExcalidrawView
- "first": meaning if there are multiple Excalidraw Views open, pick the first that is returned by `app.workspace.getLeavesOfType("Excalidraw")`
- "active": meaning the currently active view
#### getExcalidrawAPI()
```typescript
getExcalidrawAPI():any
```
Returns the native Excalidraw API (ref.current) for the active drawing specified in `targetView`.
See Excalidraw documentation here: https://www.npmjs.com/package/@excalidraw/excalidraw#ref
#### getViewElements()
```typescript
getViewElements():ExcalidrawElement[]
```
Returns all the elements from the view.
#### deleteViewElements()
```typescript
deleteViewElements(elToDelete: ExcalidrawElement[]):boolean
```
Deletes those elements from the view that match the elements provided as the input parameter.
Example to delete the selected elements from the view:
```typescript
ea = ExcalidrawAutomate;
ea.setView("active");
el = ea.getViewSelectedElements();
ea.deleteViewElements();
```
#### getViewSelectedElement()
```typescript
getViewSelectedElement():ExcalidrawElement
```
You first need to set the view calling `setView()`.
If an element is selected in the targetView the function returns the selected element. If multiple elements are selected, either by SHIFT+Clicking to select multiple elements, or by selecting a group, the first of the elements will be selected. If you want to specify which element to select from a group, double click the desired element in the group.
This function is helpful if you want to add a new element in relation to an existing element in your drawing.
#### getViewSelectedElements()
```typescript
getViewSelectedElements():ExcalidrawElement[]
```
You first need to set the view calling `setView()`.
Gets the array of selected elements in the scene. Returns [] if no elements are selected.
Note: you can call `getExcalidrawAPI().getSceneElements()` to retreive all the elements in the scene.
#### viewToggleFullScreen()
```typescript
viewToggleFullScreen(forceViewMode?:boolean):void;
```
Toggles targetView between fullscreen mode and normal mode. By setting forceViewMode to `true` will change Excalidraw mode to View mode. Default is `false`.
The function will do nothing on Obsidian Mobile.
#### connectObjectWithViewSelectedElement()
```typescript
connectObjectWithViewSelectedElement(objectA:string,connectionA: ConnectionPoint, connectionB: ConnectionPoint, formatting?:{numberOfPoints?: number,startArrowHead?:string,endArrowHead?:string, padding?: number}):boolean
```
Same as `connectObjects()`, but ObjectB is the currently selected element in the target ExcalidrawView. The function helps with placing an arrow between a newly created object and the selected element in the target ExcalidrawView.
#### addElementsToView()
```typescript
async addElementsToView(repositionToCursor:boolean=false, save:boolean=false):Promise<boolean>
```
Adds elements created with ExcalidrawAutomate to the target ExcalidrawView.
`repositionToCursor` dafault is false
- true: the elements will be moved such that the center point of the elements will be aligned with the current position of the pointer on ExcalidrawView. You can point and place elements to a desired location in your drawing using this switch.
- false: elements will be positioned as defined by the x&y coordinates of each element.
`save` default is false
- true: the drawing will be saved after the elements were added.
- false: the drawing will be saved at the next autosave cycle. Use false when adding multiple elements one after the other. Else, best to use true, to minimize risk of data loss.
### onDropHook
```typescript
onDropHook (data: {
ea: ExcalidrawAutomate,
event: React.DragEvent<HTMLDivElement>,
draggable: any, //Obsidian draggable object
type: "file"|"text"|"unknown",
payload: {
files: TFile[], //TFile[] array of dropped files
text: string, //string
},
excalidrawFile: TFile, //the file receiving the drop event
view: ExcalidrawView, //the excalidraw view receiving the drop
pointerPosition: {x:number, y:number} //the pointer position on canvas at the time of drop
}):boolean;
```
Callback function triggered when an draggable item is dropped on Excalidraw.
The function should return a boolean value. True if the drop was handled by the hook and futher native processing should be stopped, and false if Excalidraw should continue with the processing of the drop.
type of drop can be one of:
- "file" if a file from Obsidian file explorer is dropped onto Excalidraw. In this case payload.files will contain the list of files dropped.
- "text" if a link (e.g. url, or wiki link) or other text is dropped. In this case payload.text will contain the received string
- "unknown" if Excalidraw plugin does not recognize the type of dropped object. In this case you can use React.DragEvent to analysed the dropped object.
Use Templater startup templates or similar to set the Hook function.
```typescript
ea = ExcalidrawAutomate;
ea.onDropHook = (data) => {
console.log(data);
return false;
}
```

View File

@@ -0,0 +1,25 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Applying an Excalidraw Template to a New Drawing
This example is similar to the one in the introduction, only rotated 90°, and using a template, plus specifying a filename and folder to save the drawing, and opening the new drawing in a new pane.
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const ea = ExcalidrawAutomate;
ea.reset();
ea.style.angle = Math.PI/2;
ea.style.strokeWidth = 3.5;
ea.addRect(-150,-50,450,300);
ea.addText(-100,70,"Left to right");
ea.addArrow([[-100,100],[100,100]]);
ea.style.strokeColor = "red";
await ea.addText(100,-30,"top to bottom",{width:200,textAlign:"center"});
ea.addArrow([[200,0],[200,200]]);
await ea.create({
filename :"My Drawing",
foldername :"myfolder/fordemo/",
templatePath:"Excalidraw/Template2.excalidraw",
onNewPane :true});
%>
```

View File

@@ -0,0 +1,18 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Connect Objects
This [Templater](https://github.com/SilentVoid13/Templater) template demonstrates how to connect two objects using ExcalidrawAutomate.
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const ea = ExcalidrawAutomate;
ea.reset();
ea.addText(-130,-100,"Connecting two objects");
const a = ea.addRect(-100,-100,100,100);
const b = ea.addEllipse(200,200,100,100);
ea.connectObjects(a,"bottom",b,"left",{numberOfPoints: 2}); //see how the line breaks differently when moving objects around
ea.style.strokeColor = "red";
ea.connectObjects(a,"right",b,"top",1);
await ea.create();
%>
```

View File

@@ -0,0 +1,63 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Family tree from Tasklist using dataviewjs
This is similar to the mindmap script using dataviewjs, but the output is rendered vertically.
### Output
![image](https://user-images.githubusercontent.com/14358394/117549637-d3ecc280-b03b-11eb-952a-840a9a75b6ca.png)
### Input file
Task List looks like:
```markdown
- [ ] OBSIDIAN
- [ ] Silver
- [ ] PawPaw Silv
- [ ] MawMaw Silv
- [ ] Licat
- [ ] PeePaw Li
- [ ] MeeMaw Li
```
### dataviewjs script
Code to render the excalidraw looks like:
```javascript
function crawl(subtasks) {
let size = subtasks.length > 0 ? 0 : 1; //if no children then a leaf with size 1
for (let task of subtasks) {
task["size"] = crawl(task.subtasks);
size += task.size;
}
return size;
}
const tasks = dv.page("FamilyTree.md").file.tasks[0];
tasks["size"] = crawl(tasks.subtasks);
const width = 300;
const height = 150;
const ea = ExcalidrawAutomate;
ea.reset();
function buildMindmap(subtasks, depth, offset, parentObjectID) {
if (subtasks.length == 0) return;
let task;
for (let i = 0; i < subtasks.length; i++) {
task = subtasks[i]
if (depth == 1) ea.style.strokeColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,"0");
task["objectID"] = ea.addText((task.size/2+offset)*width,depth*height,task.text,{box:true})
ea.connectObjects(parentObjectID,"top",task.objectID,"bottom",{startArrowHead: 'arrow', endArrowHead: 'dot'});
if (i >= 1) {
ea.connectObjects(subtasks[i-1]['objectID'],"right",task.objectID,"left", {endArrowHead: 'none'});
}
buildMindmap(task.subtasks, depth-1,offset,task.objectID);
offset += task.size/1.5;
}
}
tasks["objectID"] = ea.addText(width*1.5,height*(tasks.size-1),tasks.text,{box:true, textAlign:"center"});
buildMindmap(tasks.subtasks, 2, 0, tasks.objectID);
ea.createSVG().then((svg)=>dv.span(svg.outerHTML));
```

View File

@@ -0,0 +1,56 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Mindmap from Tasklist using dataviewjs
This is similar to the mindmap script using templater, but because dataview already returns tasks in a tree, it is slightly simpler
### Output
![image](https://user-images.githubusercontent.com/14358394/117548665-71dd8e80-b036-11eb-8a45-4169fdd7cc05.png)
### Input file
The input file is `Demo.md` with the following contents:
```markdown
- [ ] Root task
- [ ] task 1.1
- [ ] task 1.2
- [ ] task 1.2.1
- [ ] task 1.2.2
- [ ] task 1.3
- [ ] task 1.3.1
```
### dataviewjs script
The `dataviewjs` script looks like this:
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
function crawl(subtasks) {
let size = subtasks.length > 0 ? 0 : 1; //if no children then a leaf with size 1
for (let task of subtasks) {
task["size"] = crawl(task.subtasks);
size += task.size;
}
return size;
}
const tasks = dv.page("Demo.md").file.tasks[0];
tasks["size"] = crawl(tasks.subtasks);
const width = 300;
const height = 100;
const ea = ExcalidrawAutomate;
ea.reset();
function buildMindmap(subtasks, depth, offset, parentObjectID) {
if (subtasks.length == 0) return;
for (let task of subtasks) {
if (depth == 1) ea.style.strokeColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,"0");
task["objectID"] = ea.addText(depth*width,(task.size/2+offset)*height,task.text,{box:true})
ea.connectObjects(parentObjectID,"right",task.objectID,"left",{startArrowHead: 'dot'});
buildMindmap(task.subtasks, depth+1,offset,task.objectID);
offset += task.size;
}
}
tasks["objectID"] = ea.addText(0,(tasks.size/2)*height,tasks.text,{box:true});
buildMindmap(tasks.subtasks, 1, 0, tasks.objectID);
ea.createSVG().then((svg)=>dv.span(svg.outerHTML));
```

View File

@@ -0,0 +1,23 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Insert new drawing into currently edited document
This [Templater](https://github.com/SilentVoid13/Templater) template will prompt you for the title of the drawing. It will create a new drawing with the provided title, and in the folder of the document you were editing. It will then transclude the new drawing at the cursor location and open the new drawing in a new workspace leaf by splitting the current leaf.
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const defaultTitle = tp.date.now("HHmm")+' '+tp.file.title;
const title = await tp.system.prompt("Title of the drawing?", defaultTitle);
const folder = tp.file.folder(true);
const transcludePath = (folder== '/' ? '' : folder + '/') + title + '.excalidraw';
tR = '![['+transcludePath+']]';
const ea = ExcalidrawAutomate;
ea.reset();
ea.setTheme(1); //set Theme to dark
await ea.create({
filename : title,
foldername : folder,
//templatePath: 'Excalidraw/Template.excalidraw', //uncomment if you want to use a template
onNewPane : true
});
%>
```

View File

@@ -0,0 +1,97 @@
# [◀ Excalidraw Automate How To](../readme.md)
## Generating a simple mindmap from a text outline
This is a slightly more elaborate example. This will generate an a mindmap from a tabulated outline.
### Output
![Drawing 2021-05-05 20 52 34](https://user-images.githubusercontent.com/14358394/117194124-00a69d00-ade4-11eb-8b75-5e18a9cbc3cd.png)
### Input file
Example input:
```
- Test 1
- Test 1.1
- Test 2
- Test 2.1
- Test 2.2
- Test 2.2.1
- Test 2.2.2
- Test 2.2.3
- Test 2.2.3.1
- Test 3
- Test 3.1
```
### Templater script
*Use CTRL+Shift+V to paste code into Obsidian!*
```javascript
<%*
const IDX = Object.freeze({"depth":0, "text":1, "parent":2, "size":3, "children": 4, "objectId":5});
//check if an editor is the active view
const editor = this.app.workspace.activeLeaf?.view?.editor;
if(!editor) return;
//initialize the tree with the title of the document as the first element
let tree = [[0,this.app.workspace.activeLeaf?.view?.getDisplayText(),-1,0,[],0]];
const linecount = editor.lineCount();
//helper function, use regex to calculate indentation depth, and to get line text
function getLineProps (i) {
props = editor.getLine(i).match(/^(\t*)-\s+(.*)/);
return [props[1].length+1, props[2]];
}
//a vector that will hold last valid parent for each depth
let parents = [0];
//load outline into tree
for(i=0;i<linecount;i++) {
[depth,text] = getLineProps(i);
if(depth>parents.length) parents.push(i+1);
else parents[depth] = i+1;
tree.push([depth,text,parents[depth-1],1,[]]);
tree[parents[depth-1]][IDX.children].push(i+1);
}
//recursive function to crawl the tree and identify height aka. size of each node
function crawlTree(i) {
if(i>linecount) return 0;
size = 0;
if((i+1<=linecount && tree[i+1][IDX.depth] <= tree[i][IDX.depth])|| i == linecount) { //I am a leaf
tree[i][IDX.size] = 1;
return 1;
}
tree[i][IDX.children].forEach((node)=>{
size += crawlTree(node);
});
tree[i][IDX.size] = size;
return size;
}
crawlTree(0);
//Build the mindmap in Excalidraw
const width = 300;
const height = 100;
const ea = ExcalidrawAutomate;
ea.reset();
//stores position offset of branch/leaf in height units
offsets = [0];
for(i=0;i<=linecount;i++) {
depth = tree[i][IDX.depth];
if (depth == 1) ea.style.strokeColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,"0");
tree[i][IDX.objectId] = ea.addText(depth*width,((tree[i][IDX.size]/2)+offsets[depth])*height,tree[i][IDX.text],{box:true});
//set child offset equal to parent offset
if((depth+1)>offsets.length) offsets.push(offsets[depth]);
else offsets[depth+1] = offsets[depth];
offsets[depth] += tree[i][IDX.size];
if(tree[i][IDX.parent]!=-1) {
ea.connectObjects(tree[tree[i][IDX.parent]][IDX.objectId],"right",tree[i][IDX.objectId],"left",{startArrowHead: 'dot'});
}
}
await ea.create({onNewPane: true});
%>
```

View File

@@ -0,0 +1,257 @@
# [◀ Excalidraw Automate How To](./readme.md)
[![Script Engine](https://user-images.githubusercontent.com/14358394/145684531-8d9c2992-59ac-4ebc-804a-4cce1777ded2.jpg)](https://youtu.be/hePJcObHIso)
## Introduction
Place your ExcalidrawAutomate Scripts into the folder defined in Excalidraw Settings. The Scripts folder may not be the root folder of your Vault.
![image](https://user-images.githubusercontent.com/14358394/145673547-b4f57d01-3643-40f9-abfd-14c3bfa5ab93.png)
EA scripts may be markdown files, plain text files, or .js files. The only requirement is that they must contain valid JavaScript code.
![image](https://user-images.githubusercontent.com/14358394/145673674-bb59f227-8eea-43dc-83b8-4d750e1920a8.png)
You will be able to access your scripts from Excalidraw via the Obsidian Command Palette.
![image](https://user-images.githubusercontent.com/14358394/145673652-6b1713e2-edc8-4bc8-8246-3f8df8a4b273.png)
This will allow you to assign hotkeys to your favorite scripts just like to any other Obsidian command.
![image](https://user-images.githubusercontent.com/14358394/145673633-83b6c969-cead-429b-9721-fd047f980279.png)
## Script development
An Excalidraw script will automatically receive two objects:
- `ea`: The Script Enginge will initialize the `ea` object including setting the active view to the View from which the script was called.
- `utils`: I have borrowed functions exposed on utils from [QuickAdd](https://github.com/chhoumann/quickadd/blob/master/docs/QuickAddAPI.md), though currently not all QuickAdd utility functions are implemented in Excalidraw. As of now, these are the available functions. See the example below for details.
- `inputPrompt: (header: string, placeholder?: string, value?: string)`
- Opens a prompt that asks for an input. Returns a string with the input.
- You need to await the result of inputPrompt.
- `suggester: (displayItems: string[], actualItems: string[])`
- Opens a suggester. Displays the displayItems, but you map these the other values with actualItems. Returns the selected value.
- You need to await the result of suggester.
---------
## Example Excalidraw Automate Scripts
These scripts are available as downloadable `.md` files on GitHub in [this](https://github.com/zsviczian/obsidian-excalidraw-plugin/tree/master/ea-scripts) folder 📂.
### Add box around selected elements
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-elements.jpg)
This script will add an encapsulating box around the currently selected elements in Excalidraw
```javascript
//uncomment if you want a prompt for custom padding
//const padding = parseInt (await utils.inputPrompt("padding?","number","10"));
const padding = 10
elements = ea.getViewSelectedElements();
const box = ea.getBoundingBox(elements);
color = ea
.getExcalidrawAPI()
.getAppState()
.currentItemStrokeColor;
//uncomment if you want to set the stroke to a random color
//color = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,"0");
ea.style.strokeColor = color;
id = ea.addRect(
box.topX - padding,
box.topY - padding,
box.width + 2*padding,
box.height + 2*padding
);
ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup([id].concat(elements.map((el)=>el.id)));
ea.addElementsToView(false);
```
----
### Connect selected elements with an arrow
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-connect-elements.jpg)
This script will connect two objects with an arrow. If either of the objects are a set of grouped elements (e.g. a text element grouped with an encapsulating rectangle), the script will identify these groups, and connect the arrow to the largest object in the group (assuming you want to connect the arrow to the box around the text element).
```javascript
const elements = ea.getViewSelectedElements();
ea.copyViewElementsToEAforEditing(elements);
const groups = ea.getMaximumGroups(elements);
if(groups.length !== 2) return;
els = [
ea.getLargestElement(groups[0]),
ea.getLargestElement(groups[1])
];
ea.connectObjects(
els[0].id,
null,
els[1].id,
null,
{numberOfPoints:2}
);
ea.addElementsToView();
```
----
### Reverse selected arrows
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-reverse-arrow.jpg)
Reverse the direction of **arrows** within the scope of selected elements.
```javascript
elements = ea.getViewSelectedElements().filter((el)=>el.type==="arrow");
if(!elements || elements.length===0) return;
elements.forEach((el)=>{
const start = el.startArrowhead;
el.startArrowhead = el.endArrowhead;
el.endArrowhead = start;
});
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
```
----
### Set line width of selected elements
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-stroke-width.jpg)
This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.
```javascript
let width = (ea.getViewSelectedElement().strokeWidth??1).toString();
width = await utils.inputPrompt("Width?","number",width);
const elements=ea.getViewSelectedElements();
ea.copyViewElementsToEAforEditing(elements);
ea.getElements().forEach((el)=>el.strokeWidth=width);
ea.addElementsToView();
```
----
### Set grid size
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid.jpg)
The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface.
```javascript
const grid = parseInt(await utils.inputPrompt("Grid size?",null,"20"));
const api = ea.getExcalidrawAPI();
let appState = api.getAppState();
appState.gridSize = grid;
api.updateScene({
appState,
commitToHistory:false
});
```
----
### Set element dimensions and position
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-dimensions.jpg)
Currently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.
```javascript
const elements = ea.getViewSelectedElements();
if(elements.length === 0) return;
const el = ea.getLargestElement(elements);
const sizeIn = [el.x,el.y,el.width,el.height].join(",");
let res = await utils.inputPrompt("x,y,width,height?",null,sizeIn);
res = res.split(",");
if(res.length !== 4) return;
let size = [];
for (v of res) {
const i = parseInt(v);
if(isNaN(i)) return;
size.push(i);
}
el.x = size[0];
el.y = size[1];
el.width = size[2];
el.height = size[3];
ea.copyViewElementsToEAforEditing([el]);
ea.addElementsToView();
```
----
### Bullet points
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-bullet-point.jpg)
This script will add a small circle to the top left of each text element in the selection and add the text and the "bullet point" into a group.
```javascript
elements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
ea.copyViewElementsToEAforEditing(elements);
const padding = 10;
elements.forEach((el)=>{
ea.style.strokeColor = el.strokeColor;
const size = el.fontSize/2;
const ellipseId = ea.addEllipse(
el.x-padding-size,
el.y+size/2,
size,
size
);
ea.addToGroup([el.id,ellipseId]);
});
ea.addElementsToView();
```
----
### Split text by lines
**!!!Requires Excalidraw 1.5.1 or higher**
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-split-lines.jpg)
Split lines of text into separate text elements for easier reorganization
```javascript
elements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
elements.forEach((el)=>{
ea.style.strokeColor = el.strokeColor;
ea.style.fontFamily = el.fontFamily;
ea.style.fontSize = el.fontSize;
const text = el.text.split("\n");
for(i=0;i<text.length;i++) {
ea.addText(el.x,el.y+i*el.height/text.length,text[i]);
}
});
ea.addElementsToView();
ea.deleteViewElements(elements);
```
----
### Set Text Alignment
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-align.jpg)
Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.
```javascript
elements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
if(elements.length===0) return;
let align = ["left","right","center"];
align = await utils.suggester(align,align);
elements.forEach((el)=>el.textAlign = align);
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
```
----
### Set Font Family
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-font-family.jpg)
Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.
```javascript
elements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
if(elements.length===0) return;
let font = ["Virgil","Helvetica","Cascadia"];
font = parseInt(await utils.suggester(font,["1","2","3"]));
if (isNaN(font)) return;
elements.forEach((el)=>el.fontFamily = font);
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();
```

1
docs/_config.yml Normal file
View File

@@ -0,0 +1 @@
theme: jekyll-theme-hacker

38
docs/readme.md Normal file
View File

@@ -0,0 +1,38 @@
# Excalidraw Automate How To
Use ExcalidrawAutomate to create or manipulate Excalidraw drawings using the [ExcalidrawAutomate Script Engine](ExcalidrawScriptsEngine.md), the [Templater](https://silentvoid13.github.io/Templater/docs/) or the [QuickAdd](https://github.com/chhoumann/quickadd) plugins, and to generate embedded SVG and PNG images using [DataviewJS](https://blacksmithgu.github.io/obsidian-dataview/docs/api/intro/)
With a little work, using ExcalidrawAutomate you can generate simple mindmaps, build a family tree, fill out SVG forms, create customized charts, or automate simple tasks (i.e. create macros) in Excalidraw.
![image](https://user-images.githubusercontent.com/14358394/117549619-bae41180-b03b-11eb-968d-c909e79a7524.png)
## API documentation
- **start here** [Introduction to the API](API/introduction.md)
- [Overview of Attributes and Functions](API/attributes_functions_overview.md)
- [Element Sytle](API/element_style.md)
- [Canvas Style](API/canvas_style.md)
- [Adding Objects](API/objects.md)
- [Utility Functions](API/utility.md)
## ExcalidrawAutomate Script Engine
I recommend using the Scripts Engine for "Macro" like automation, when you want to automate a few simple steps, such as adding a box around a text element, or connecting two objects with an arrow, or setting line width or the grid to a custom value.
- [ExcalidrawAutomate Script Engine](ExcalidrawScriptsEngine.md).
## Examples
- **Templater**
- [Insert new drawing into currently edited document](Examples/insert_new_drawing.md)
- [Connect objects](Examples/connect_objects.md)
- [Apply an Excalidraw template](Examples/apply_template.md)
- [Mindmap with Templater](Examples/templater_mindmap.md)
- **Dataview**
- [Mindmap with Dataview](Examples/dataviewjs_mindmap.md)
- [Family tree with Dataview](Examples/dataviewjs_familytree.md)
## If you are enjoying the Obsidian Excalidraw Plugin...
Help spread the word by sharing about the Plugin on social media.
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="150">](https://ko-fi.com/zsolt)

View File

@@ -0,0 +1,40 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-elements.jpg)
This script will add an encapsulating box around the currently selected elements in Excalidraw.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
//uncomment if you don't want a prompt for custom padding
//const padding = 10
const padding = parseInt (await utils.inputPrompt("padding?","number","10"));
if(isNaN(padding)) {
new Notice("The padding value provided is not a number");
return;
}
elements = ea.getViewSelectedElements();
const box = ea.getBoundingBox(elements);
color = ea
.getExcalidrawAPI()
.getAppState()
.currentItemStrokeColor;
//uncomment for random color:
//color = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,"0");
ea.style.strokeColor = color;
id = ea.addRect(
box.topX - padding,
box.topY - padding,
box.width + 2*padding,
box.height + 2*padding
);
ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup([id].concat(elements.map((el)=>el.id)));
ea.addElementsToView(false);

View File

@@ -0,0 +1,30 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-bullet-point.jpg)
This script will add a small circle to the top left of each text element in the selection and add the text and the "bullet point" into a group.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
elements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
ea.copyViewElementsToEAforEditing(elements);
const padding = 10;
elements.forEach((el)=>{
ea.style.strokeColor = el.strokeColor;
const size = el.fontSize/2;
const ellipseId = ea.addEllipse(
el.x-padding-size,
el.y+size/2,
size,
size
);
ea.addToGroup([el.id,ellipseId]);
});
ea.addElementsToView();

View File

@@ -0,0 +1,35 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-connect-elements.jpg)
This script will connect two objects with an arrow. If either of the objects are a set of grouped elements (e.g. a text element grouped with an encapsulating rectangle), the script will identify these groups, and connect the arrow to the largest object in the group (assuming you want to connect the arrow to the box around the text element).
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
ea.copyViewElementsToEAforEditing(elements);
const groups = ea.getMaximumGroups(elements);
if(groups.length !== 2) return;
els = [
ea.getLargestElement(groups[0]),
ea.getLargestElement(groups[1])
];
ea.connectObjects(
els[0].id,
null,
els[1].id,
null,
{
endArrowHead: "triangle",
startArrowHead: "dot",
numberOfPoints: 2
}
);
ea.addElementsToView();

View File

@@ -0,0 +1,29 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
Converts text elements to links pointing to a file in a selected folder and with the alias set as the original text. The script will prompt the user to select an existing folder from the vault.
`original text` => `[[selected folder/original text|original text]]`
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
folders = new Set();
app.vault.getFiles().forEach((f)=>
folders.add(f.path.substring(0,f.path.lastIndexOf("/")))
);
f = Array.from(folders);
folder = await utils.suggester(f,f);
folder = folder === "" ? folder : folder + "/";
elements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
elements.forEach((el)=>{
el.rawText = "[["+folder+el.rawText+"|"+el.rawText+"]]";
})
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

View File

@@ -0,0 +1,21 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-create-and-embed-new-markdown-file.jpg)
The script will prompt you for a filename, then create a new markdown document with the file name provided, open the new markdown document in an adjacent pane, and embed the markdown document into the active Excalidraw drawing.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
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",folder);
const file = await app.fileManager.createAndOpenMarkdownFile(fname,true);
await ea.addImage(0,0,file);
ea.addElementsToView(true,true);

View File

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

39
ea-scripts/Dimensions.md Normal file
View File

@@ -0,0 +1,39 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-dimensions.jpg)
Currently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
if(elements.length === 0) return;
const el = ea.getLargestElement(elements);
const sizeIn = [
Math.round(el.x),
Math.round(el.y),
Math.round(el.width),
Math.round(el.height)
].join(",");
let res = await utils.inputPrompt("x,y,width,height?",null,sizeIn);
res = res.split(",");
if(res.length !== 4) return;
let size = [];
for (v of res) {
const i = parseInt(v);
if(isNaN(i)) return;
size.push(i);
}
el.x = size[0];
el.y = size[1];
el.width = size[2];
el.height = size[3];
ea.copyViewElementsToEAforEditing([el]);
ea.addElementsToView();

View File

@@ -0,0 +1,56 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/elbow-connectors.png)
This script converts the selected connectors to elbows.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const lines = elements.filter((el)=>el.type==="arrow" || el.type==="line");
for (const line of lines) {
if (line.points.length >= 3) {
for (var i = 0; i < line.points.length - 2; i++) {
var p1;
var p3;
if (line.points[i][0] < line.points[i + 2][0]) {
p1 = line.points[i];
p3 = line.points[i+2];
} else {
p1 = line.points[i + 2];
p3 = line.points[i];
}
const p2 = line.points[i + 1];
if (p1[0] === p3[0]) {
continue;
}
const k = (p3[1] - p1[1]) / (p3[0] - p1[0]);
const b = p1[1] - k * p1[0];
y0 = k * p2[0] + b;
const up = p2[1] < y0;
if ((k > 0 && !up) || (k < 0 && up)) {
p2[0] = p1[0];
p2[1] = p3[1];
} else {
p2[0] = p3[0];
p2[1] = p1[1];
}
}
}
}
ea.copyViewElementsToEAforEditing(lines);
ea.addElementsToView();

View File

@@ -0,0 +1,75 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)
This script expands the width of the selected rectangles until they are all the same width and keep the text centered.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const groupWidths = topGroups
.map((g) =>
g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minLeft: cur.x,
maxRight: cur.x + cur.width,
index: i,
};
} else {
return {
minLeft: cur.x < pre.minLeft ? cur.x : pre.minLeft,
maxRight:
cur.x + cur.width > pre.maxRight
? cur.x + cur.width
: pre.maxRight,
index: i,
};
}
},
{ minLeft: 0, maxRight: 0 }
)
)
.map((r) => {
r.width = r.maxRight - r.minLeft;
return r;
});
const maxGroupWidth = Math.max(...groupWidths.map((g) => g.width));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.x - rha.x);
const texts = topGroups[i]
.filter((el) => el.type === "text")
.sort((lha, rha) => lha.x - rha.x);
const groupWith = groupWidths[i].width;
if (groupWith < maxGroupWidth) {
const distance = maxGroupWidth - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
rect.x = rect.x + perRectDistance * j - perRectDistance / 2;
rect.width += perRectDistance;
}
for (var j = 0; j < texts.length; j++) {
const text = texts[j];
text.x = text.x + perRectDistance * j;
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

View File

@@ -0,0 +1,76 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)
This script expands the width of the selected rectangles until they are all the same width.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const groupWidths = topGroups
.map((g) =>
g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minLeft: cur.x,
maxRight: cur.x + cur.width,
index: i,
};
} else {
return {
minLeft: cur.x < pre.minLeft ? cur.x : pre.minLeft,
maxRight:
cur.x + cur.width > pre.maxRight
? cur.x + cur.width
: pre.maxRight,
index: i,
};
}
},
{ minLeft: 0, maxRight: 0 }
)
)
.map((r) => {
r.width = r.maxRight - r.minLeft;
return r;
});
const maxGroupWidth = Math.max(...groupWidths.map((g) => g.width));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.x - rha.x);
const texts = topGroups[i]
.filter((el) => el.type === "text")
.sort((lha, rha) => lha.x - rha.x);
const groupWith = groupWidths[i].width;
if (groupWith < maxGroupWidth) {
const distance = maxGroupWidth - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
rect.x = rect.x + perRectDistance * j;
rect.width += perRectDistance;
}
for (var j = 0; j < texts.length; j++) {
const text = texts[j];
text.x = text.x + perRectDistance * j;
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

View File

@@ -0,0 +1,75 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)
This script expands the height of the selected rectangles until they are all the same height and keep the text centered.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const groupHeights = topGroups
.map((g) =>
g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minTop: cur.y,
maxBottom: cur.y + cur.height,
index: i,
};
} else {
return {
minTop: cur.y < pre.minTop ? cur.y : pre.minTop,
maxBottom:
cur.y + cur.height > pre.maxBottom
? cur.y + cur.height
: pre.maxBottom,
index: i,
};
}
},
{ minTop: 0, maxBottom: 0 }
)
)
.map((r) => {
r.height = r.maxBottom - r.minTop;
return r;
});
const maxGroupHeight = Math.max(...groupHeights.map((g) => g.height));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.y - rha.y);
const texts = topGroups[i]
.filter((el) => el.type === "text")
.sort((lha, rha) => lha.y - rha.y);
const groupWith = groupHeights[i].height;
if (groupWith < maxGroupHeight) {
const distance = maxGroupHeight - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
rect.y = rect.y + perRectDistance * j - perRectDistance / 2;
rect.height += perRectDistance;
}
for (var j = 0; j < texts.length; j++) {
const text = texts[j];
text.y = text.y + perRectDistance * j;
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

View File

@@ -0,0 +1,73 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)
This script expands the height of the selected rectangles until they are all the same height.
```javascript
*/
const elements = ea.getViewSelectedElements();
const topGroups = ea.getMaximumGroups(elements);
const groupHeights = topGroups
.map((g) =>
g.reduce(
(pre, cur, i) => {
if (i === 0) {
return {
minTop: cur.y,
maxBottom: cur.y + cur.height,
index: i,
};
} else {
return {
minTop: cur.y < pre.minTop ? cur.y : pre.minTop,
maxBottom:
cur.y + cur.height > pre.maxBottom
? cur.y + cur.height
: pre.maxBottom,
index: i,
};
}
},
{ minTop: 0, maxBottom: 0 }
)
)
.map((r) => {
r.height = r.maxBottom - r.minTop;
return r;
});
const maxGroupHeight = Math.max(...groupHeights.map((g) => g.height));
for (var i = 0; i < topGroups.length; i++) {
const rects = topGroups[i]
.filter((el) => el.type === "rectangle")
.sort((lha, rha) => lha.y - rha.y);
const texts = topGroups[i]
.filter((el) => el.type === "text")
.sort((lha, rha) => lha.y - rha.y);
const groupWith = groupHeights[i].height;
if (groupWith < maxGroupHeight) {
const distance = maxGroupHeight - groupWith;
const perRectDistance = distance / rects.length;
for (var j = 0; j < rects.length; j++) {
const rect = rects[j];
rect.y = rect.y + perRectDistance * j;
rect.height += perRectDistance;
}
for (var j = 0; j < texts.length; j++) {
const text = texts[j];
text.y = text.y + perRectDistance * j;
}
}
}
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

View File

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

View File

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

22
ea-scripts/Font Family.md Normal file
View File

@@ -0,0 +1,22 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-font-family.jpg)
Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
elements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
if(elements.length===0) return;
let font = ["Virgil","Helvetica","Cascadia"];
font = parseInt(await utils.suggester(font,["1","2","3"]));
if (isNaN(font)) return;
elements.forEach((el)=>el.fontFamily = font);
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

24
ea-scripts/Grid.md Normal file
View File

@@ -0,0 +1,24 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid.jpg)
The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const grid = parseInt(await utils.inputPrompt("Grid size?",null,"20"));
if(isNaN(grid)) return; //this is to avoid passing an illegal value to Excalidraw
const api = ea.getExcalidrawAPI();
let appState = api.getAppState();
appState.gridSize = grid;
api.updateScene({
appState,
commitToHistory:false
});

View File

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

View File

@@ -0,0 +1,187 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-modify-background-color-opacity.png)
This script changes the opacity of the background color of the selected boxes.
The default background color in Excalidraw is so dark that the text is hard to read. You can lighten the color a bit by setting transparency. And you can tweak the transparency over and over again until you're happy with it.
Although excalidraw has the opacity option in its native property Settings, it also changes the transparency of the border. Use this script to change only the opacity of the background color without affecting the border.
```javascript
*/
const alpha = parseFloat(await utils.inputPrompt("Background color opacity?","number","0.6"));
if(isNaN(alpha)) {
return;
}
const elements=ea.getViewSelectedElements().filter((el)=>["rectangle","ellipse","diamond","line","image"].includes(el.type));
ea.copyViewElementsToEAforEditing(elements);
ea.getElements().forEach((el)=>{
const color = colorNameToHex(el.backgroundColor);
const rgbColor = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
if(rgbColor) {
const r = parseInt(rgbColor[1], 16);
const g = parseInt(rgbColor[2], 16);
const b = parseInt(rgbColor[3], 16);
el.backgroundColor=`rgba(${r},${g},${b},${alpha})`;
}
else {
const rgbaColor = /^rgba\((\d+,\d+,\d+,)(\d*\.?\d*)\)$/i.exec(color);
if(rgbaColor) {
el.backgroundColor=`rgba(${rgbaColor[1]}${alpha})`;
}
}
});
ea.addElementsToView();
function colorNameToHex(color) {
const colors = {
"aliceblue":"#f0f8ff",
"antiquewhite":"#faebd7",
"aqua":"#00ffff",
"aquamarine":"#7fffd4",
"azure":"#f0ffff",
"beige":"#f5f5dc",
"bisque":"#ffe4c4",
"black":"#000000",
"blanchedalmond":"#ffebcd",
"blue":"#0000ff",
"blueviolet":"#8a2be2",
"brown":"#a52a2a",
"burlywood":"#deb887",
"cadetblue":"#5f9ea0",
"chartreuse":"#7fff00",
"chocolate":"#d2691e",
"coral":"#ff7f50",
"cornflowerblue":"#6495ed",
"cornsilk":"#fff8dc",
"crimson":"#dc143c",
"cyan":"#00ffff",
"darkblue":"#00008b",
"darkcyan":"#008b8b",
"darkgoldenrod":"#b8860b",
"darkgray":"#a9a9a9",
"darkgreen":"#006400",
"darkkhaki":"#bdb76b",
"darkmagenta":"#8b008b",
"darkolivegreen":"#556b2f",
"darkorange":"#ff8c00",
"darkorchid":"#9932cc",
"darkred":"#8b0000",
"darksalmon":"#e9967a",
"darkseagreen":"#8fbc8f",
"darkslateblue":"#483d8b",
"darkslategray":"#2f4f4f",
"darkturquoise":"#00ced1",
"darkviolet":"#9400d3",
"deeppink":"#ff1493",
"deepskyblue":"#00bfff",
"dimgray":"#696969",
"dodgerblue":"#1e90ff",
"firebrick":"#b22222",
"floralwhite":"#fffaf0",
"forestgreen":"#228b22",
"fuchsia":"#ff00ff",
"gainsboro":"#dcdcdc",
"ghostwhite":"#f8f8ff",
"gold":"#ffd700",
"goldenrod":"#daa520",
"gray":"#808080",
"green":"#008000",
"greenyellow":"#adff2f",
"honeydew":"#f0fff0",
"hotpink":"#ff69b4",
"indianred ":"#cd5c5c",
"indigo":"#4b0082",
"ivory":"#fffff0",
"khaki":"#f0e68c",
"lavender":"#e6e6fa",
"lavenderblush":"#fff0f5",
"lawngreen":"#7cfc00",
"lemonchiffon":"#fffacd",
"lightblue":"#add8e6",
"lightcoral":"#f08080",
"lightcyan":"#e0ffff",
"lightgoldenrodyellow":"#fafad2",
"lightgrey":"#d3d3d3",
"lightgreen":"#90ee90",
"lightpink":"#ffb6c1",
"lightsalmon":"#ffa07a",
"lightseagreen":"#20b2aa",
"lightskyblue":"#87cefa",
"lightslategray":"#778899",
"lightsteelblue":"#b0c4de",
"lightyellow":"#ffffe0",
"lime":"#00ff00",
"limegreen":"#32cd32",
"linen":"#faf0e6",
"magenta":"#ff00ff",
"maroon":"#800000",
"mediumaquamarine":"#66cdaa",
"mediumblue":"#0000cd",
"mediumorchid":"#ba55d3",
"mediumpurple":"#9370d8",
"mediumseagreen":"#3cb371",
"mediumslateblue":"#7b68ee",
"mediumspringgreen":"#00fa9a",
"mediumturquoise":"#48d1cc",
"mediumvioletred":"#c71585",
"midnightblue":"#191970",
"mintcream":"#f5fffa",
"mistyrose":"#ffe4e1",
"moccasin":"#ffe4b5",
"navajowhite":"#ffdead",
"navy":"#000080",
"oldlace":"#fdf5e6",
"olive":"#808000",
"olivedrab":"#6b8e23",
"orange":"#ffa500",
"orangered":"#ff4500",
"orchid":"#da70d6",
"palegoldenrod":"#eee8aa",
"palegreen":"#98fb98",
"paleturquoise":"#afeeee",
"palevioletred":"#d87093",
"papayawhip":"#ffefd5",
"peachpuff":"#ffdab9",
"peru":"#cd853f",
"pink":"#ffc0cb",
"plum":"#dda0dd",
"powderblue":"#b0e0e6",
"purple":"#800080",
"rebeccapurple":"#663399",
"red":"#ff0000",
"rosybrown":"#bc8f8f",
"royalblue":"#4169e1",
"saddlebrown":"#8b4513",
"salmon":"#fa8072",
"sandybrown":"#f4a460",
"seagreen":"#2e8b57",
"seashell":"#fff5ee",
"sienna":"#a0522d",
"silver":"#c0c0c0",
"skyblue":"#87ceeb",
"slateblue":"#6a5acd",
"slategray":"#708090",
"snow":"#fffafa",
"springgreen":"#00ff7f",
"steelblue":"#4682b4",
"tan":"#d2b48c",
"teal":"#008080",
"thistle":"#d8bfd8",
"tomato":"#ff6347",
"turquoise":"#40e0d0",
"violet":"#ee82ee",
"wheat":"#f5deb3",
"white":"#ffffff",
"whitesmoke":"#f5f5f5",
"yellow":"#ffff00",
"yellowgreen":"#9acd32"
};
if (typeof colors[color.toLowerCase()] != 'undefined')
return colors[color.toLowerCase()];
return color;
}

View File

@@ -0,0 +1,21 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-stroke-width.jpg)
This script will set the stroke width of selected elements. This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
let width = (ea.getViewSelectedElement().strokeWidth??1).toString();
width = await utils.inputPrompt("Width?","number",width);
const elements=ea.getViewSelectedElements();
ea.copyViewElementsToEAforEditing(elements);
ea.getElements().forEach((el)=>el.strokeWidth=width);
ea.addElementsToView();

View File

@@ -0,0 +1,121 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-ocr.jpg)
THIS SCRIPT REQUIRES EXCALIDRAW 1.5.15
The script will
1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and
2) will add the text to your drawing as a text element
I recommend also installing the [Transfer TextElements to Excalidraw markdown metadata](Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md) script as well.
The script is based on [@schlundd](https://github.com/schlundd)'s [Obsidian-OCR-Plugin](https://github.com/schlundd/obsidian-ocr-plugin)
See ScriptEngine documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const curVersion = app.plugins.manifests["obsidian-excalidraw-plugin"].version;
if(curVersion < "1.5.15") new Notice("please update Excalidraw plugin to the latest version");
let token = ea.getScriptSettings()?.token;
const BASE_URL = "https://ocr.taskbone.com";
//get new token if token was not provided
if (!token) {
const tokenResponse = await fetch(
BASE_URL + "/get-new-token", {
method: 'post'
});
if (tokenResponse.status === 200) {
jsonResponse = await tokenResponse.json();
token = jsonResponse.token;
ea.setScriptSettings({token});
} else {
notice(`Taskbone OCR Error: ${tokenResponse.status}\nPlease try again later.`);
return;
}
}
//get image element
//if multiple image elements were selected prompt user to choose
const imageElements = ea.getViewSelectedElements().filter((el)=>el.type==="image");
//need to save the view to ensure recently pasted images are saved as files
await ea.targetView.save();
let selectedImageElement = null;
switch (imageElements.length) {
case 0:
return;
case 1:
selectedImageElement = imageElements[0];
break;
default:
const files = imageElements.map((el)=>ea.getViewFileForImageElement(el));
selectedImageElement = await utils.suggester(files.map((f)=>f.name),imageElements);
break;
}
if(!selectedImageElement) {
notice("No image element was selected");
return;
}
const imageFile = ea.getViewFileForImageElement(selectedImageElement);
if(!imageFile) {
notice("Can read image file");
return;
}
//Execute the OCR
let text = null;
const fileBuffer = await app.vault.readBinary(imageFile);
const formData = new FormData();
formData.append("image", new Blob([fileBuffer]))
try {
const response = await fetch(
BASE_URL + "/get-text", {
headers: {
Authorization: "Bearer " + token
},
method: "post",
body: formData
});
if (response.status == 200) {
jsonResponse = await response.json();
text = jsonResponse?.text;
} else {
notice(`Could not read Text from ${file.path}:\n Error: ${response.status}`);
return;
}
} catch (error) {
notice(`The OCR service seems unavailable right now. Please try again later.`);
return;
}
if(!text) {
notice("No text found");
return;
}
console.log({text});
//add text element to drawing
const id = ea.addText(selectedImageElement.x,selectedImageElement.y+selectedImageElement.height,text);
await ea.addElementsToView();
const API = ea.getExcalidrawAPI();
st = API.getAppState();
st.selectedElementIds = {};
st.selectedElementIds[id] = true;
API.updateScene({appState: st});
API.zoomToFit(ea.getViewSelectedElements(),1);
//utility function
function notice(message) {
new Notice(message,10000);
console.log(message);
}

42
ea-scripts/README.md Normal file
View File

@@ -0,0 +1,42 @@
# Excalidraw Script Engine scripts library
Click to watch the intro video:
[![Script Engine](https://user-images.githubusercontent.com/14358394/145684531-8d9c2992-59ac-4ebc-804a-4cce1777ded2.jpg)](https://youtu.be/hePJcObHIso)
See the [Excalidraw Script Engine](https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html) documentation for more details.
## How to install scripts into your Obsidian Vault
Open the script you are interested in and save it to your Obsidian Vault including the first line `/*`, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
## List of available scripts
|Title|Description|Icon|Contributor|
|----|----|----|----|
|[Box Selected Elements](Box%20Selected%20Elements.md)|This script will add an encapsulating box around the currently selected elements in Excalidraw.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-elements.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Bullet Point](Bullet%20Point.md)|This script will add a small circle to the top left of each text element in the selection and add the text and the "bullet point" into a group.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-bullet-point.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Connect elements](Connect%20elements.md)|This script will connect two objects with an arrow. If either of the objects are a set of grouped elements (e.g. a text element grouped with an encapsulating rectangle), the script will identify these groups, and connect the arrow to the largest object in the group (assuming you want to connect the arrow to the box around the text element).|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-connect-elements.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Convert text to link with folder and alias](Convert%20text%20to%20link%20with%20folder%20and%20alias.md)|Converts text elements to links pointing to a file in a selected folder and with the alias set as the original text. The script will prompt the user to select an existing folder from the vault.|`original text` => `[[selected folder/original text\|original text]]`|[@zsviczian](https://github.com/zsviczian)|
|[Create new markdown file and embed into active drawing](Create%20new%20markdown%20file%20and%20embed%20into%20active%20drawing.md)|The script will prompt you for a filename, then create a new markdown document with the file name provided, open the new markdown document in an adjacent pane, and embed the markdown document into the active Excalidraw drawing.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-create-and-embed-new-markdown-file.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Darken background color](Darken%20background%20color.md)|This script darkens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect. In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png)|[@1-2-3](https://github.com/1-2-3)|
|[Dimensions](Dimensions.md)|Currently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-dimensions.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Elbow connectors](Elbow%20connectors.md)|This script converts the selected connectors to elbows.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/elbow-connectors.png)|[@1-2-3](https://github.com/1-2-3)|
|[Expand rectangles horizontally keep text centered](Expand%20rectangles%20horizontally%20keep%20text20%centered.md)|This script expands the width of the selected rectangles until they are all the same width and keep the text centered.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)|[@1-2-3](https://github.com/1-2-3)|
|[Expand rectangles horizontally](Expand%20rectangles%20horizontally.md)|This script expands the width of the selected rectangles until they are all the same width.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)|[@1-2-3](https://github.com/1-2-3)|
|[Expand rectangles vertically keep text centered](Expand%20rectangles%20vertically%20keep%20text%20centered.md)|This script expands the height of the selected rectangles until they are all the same height and keep the text centered.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)|[@1-2-3](https://github.com/1-2-3)|
|[Expand rectangles vertically](Expand%20rectangles%20vertically.md)|This script expands the height of the selected rectangles until they are all the same height.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif)|[@1-2-3](https://github.com/1-2-3)|
|[Fixed spacing](Fixed%20spacing.md)|The script arranges the selected elements horizontally with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fix-space-demo.png)|[@1-2-3](https://github.com/1-2-3)|
|[Fixed vertical distance](Fixed%20vertical%20distance.md)|The script arranges the selected elements vertically with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-vertical-distance.png)|[@1-2-3](https://github.com/1-2-3)|
|[Font Family](Font%20Family.md)|Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-font-family.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Grid](Grid.md)|The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Lighten background color](Lighten%20background%20color.md)|This script lightens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect.In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png)|[@1-2-3](https://github.com/1-2-3)|
|[Modify background color opacity](Modify%20background%20color%20opacity.md)|This script changes the opacity of the background color of the selected boxes. The default background color in Excalidraw is so dark that the text is hard to read. You can lighten the color a bit by setting transparency. And you can tweak the transparency over and over again until you're happy with it. Although excalidraw has the opacity option in its native property Settings, it also changes the transparency of the border. Use this script to change only the opacity of the background color without affecting the border.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-modify-background-color-opacity.png)|[@1-2-3](https://github.com/1-2-3)|
|[Modify stroke width of selected elements](Modify%20stroke%20width%20of%20selected%20elements.md)|This script will set the stroke width of selected elements. This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-stroke-width.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[OCR - Optical Character Recognition](OCR%20-%20Optical%20Character%20Recognition.md)|The script will 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and 2) will add the text to your drawing as a text element.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-ocr.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Reverse arrows](Reverse%20arrows.md)|Reverse the direction of **arrows** within the scope of selected elements.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-reverse-arrow.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Set Link Alias](Set20%Link20%Alias.md)|Iterates all of the links in the selected TextElements and prompts the user to set or modify the alias for each link found.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-set-link-alias.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Split text by lines](Split%20text%20by%20lines.md)|Split lines of text into separate text elements for easier reorganization|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-split-lines.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Text Align](Text%20Align.md)|Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-align.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Transfer TextElements to Excalidraw markdown metadata](Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md)|The script will delete the selected text elements from the canvas and will copy the text from these text elements into the Excalidraw markdown file as metadata. This means, that the text will no longer be visible in the drawing, however you will be able to search for the text in Obsidian and find the drawing containing this image.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-to-metadata.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Zoom to Fit Selected Elements](Zoom%20to%20Fit%20Selected%20Elements.md)|Similar to Excalidraw standard SHIFT+2 feature: Zoom to fit selected elements, but with the ability to zoom to 1000%. Inspiration: [#272](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/272)||[@zsviczian](https://github.com/zsviczian)|

View File

@@ -0,0 +1,23 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-reverse-arrow.jpg)
Reverse the direction of **arrows** within the scope of selected elements.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
elements = ea.getViewSelectedElements().filter((el)=>el.type==="arrow");
if(!elements || elements.length===0) return;
elements.forEach((el)=>{
const start = el.startArrowhead;
el.startArrowhead = el.endArrowhead;
el.endArrowhead = start;
});
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

View File

@@ -0,0 +1,53 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-set-link-alias.jpg)
Iterates all of the links in the selected TextElements and prompts the user to set or modify the alias for each link found.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
elements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
// `[[markdown links]]`
for(el of elements) { //doing for instead of .forEach due to await inputPrompt
parts = el.rawText.split(/(\[\[[\w\W]*?]])/);
newText = "";
for(t of parts) { //doing for instead of .map due to await inputPrompt
if(!t.match(/(\[\[[\w\W]*?]])/)) {
newText += t;
} else {
original = t.split(/\[\[|]]/)[1];
cut = original.indexOf("|");
alias = cut === -1 ? "" : original.substring(cut+1);
link = cut === -1 ? original : original.substring(0,cut);
alias = await utils.inputPrompt(`Alias for [[${link}]]`,"type alias here",alias);
newText += `[[${link}|${alias}]]`;
}
}
el.rawText = newText;
};
// `[wiki](links)`
for(el of elements) { //doing for instead of .forEach due to await inputPrompt
parts = el.rawText.split(/(\[[\w\W]*?]\([\w\W]*?\))/);
newText = "";
for(t of parts) { //doing for instead of .map due to await inputPrompt
if(!t.match(/(\[[\w\W]*?]\([\w\W]*?\))/)) {
newText += t;
} else {
alias = t.match(/\[([\w\W]*?)]/)[1];
link = t.match(/\(([\w\W]*?)\)/)[1];
alias = await utils.inputPrompt(`Alias for [[${link}]]`,"type alias here",alias);
newText += `[[${link}|${alias}]]`;
}
}
el.rawText = newText;
};
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

View File

@@ -0,0 +1,27 @@
/*
## requires Excalidraw 1.5.1 or higher
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-split-lines.jpg)
Split lines of text into separate text elements for easier reorganization
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
elements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
elements.forEach((el)=>{
ea.style.strokeColor = el.strokeColor;
ea.style.fontFamily = el.fontFamily;
ea.style.fontSize = el.fontSize;
const text = el.text.split("\n");
for(i=0;i<text.length;i++) {
ea.addText(el.x,el.y+i*el.height/text.length,text[i]);
}
});
ea.addElementsToView();
ea.deleteViewElements(elements);

21
ea-scripts/Text Align.md Normal file
View File

@@ -0,0 +1,21 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-align.jpg)
Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
elements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
if(elements.length===0) return;
let align = ["left","right","center"];
align = await utils.suggester(align,align);
elements.forEach((el)=>el.textAlign = align);
ea.copyViewElementsToEAforEditing(elements);
ea.addElementsToView();

View File

@@ -0,0 +1,44 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-to-metadata.jpg)
The script will delete the selected text elements from the canvas and will copy the text from these text elements into the Excalidraw markdown file as metadata. This means, that the text will no longer be visible in the drawing, however you will be able to search for the text in Obsidian and find the drawing containing this image.
See ScriptEngine documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
//get text elements
const textElements = ea.getViewSelectedElements().filter((el)=>el.type==="text");
if(textElements.length===0) {
notice("No text elements were selected")
return;
}
metadata = "# Metadata\n" + textElements
.map((el)=>el.rawText.replaceAll(/%|\^/g,"_")) //cleaning these characters for safety, might not be needed
.join("/n") + "\n";
ea.deleteViewElements(textElements);
await ea.targetView.save();
data = await app.vault.read(ea.targetView.file);
splitAfterFrontmatter = data.split(/(^---[\w\W]*?---\n)/);
if(splitAfterFrontmatter.length !== 3) {
notice("Error locating frontmatter in markdown file");
console.log({file:ea.targetView.file});
return;
}
newData = splitAfterFrontmatter[1]+metadata+splitAfterFrontmatter[2]
await app.vault.modify(ea.targetView.file,newData);
//utility function
function notice(message) {
new Notice(message);
console.log(message);
}

View File

@@ -0,0 +1,15 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-download-raw.jpg)
Download this file and save to your Obsidian Vault including the first line, or open it in "Raw" and copy the entire contents to Obsidian.
Similar to Excalidraw standard SHIFT+2 feature: Zoom to fit selected elements, but with the ability to zoom to 1000%. Inspiration: [#272](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/272)
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
elements = ea.getViewSelectedElements();
api = ea.getExcalidrawAPI();
api.zoomToFit(elements,10);

210
ea-scripts/index.md Normal file
View File

@@ -0,0 +1,210 @@
If you are enjoying the Excalidraw plugin then please support my work and enthusiasm by buying me a coffee on [https://ko-fi/zsolt](https://ko-fi.com/zsolt).
[<img src="https://user-images.githubusercontent.com/14358394/115450238-f39e8100-a21b-11eb-89d0-fa4b82cdbce8.png" class="coffee">](https://ko-fi.com/zsolt)
---
Jump ahead to the [[#List of available scripts]]
# Intorducing Excalidraw Automate Script Engine
<iframe width="560" height="315" src="https://www.youtube.com/embed/hePJcObHIso" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
Script Engine scripts are installed in the `Downloaded` subfolder of the `Excalidraw Automate script folder` specified in plugin settings.
In the `Command Palette` installed scripts are prefixed with `Downloaded/`, thus you can always know if you are executing a local script of your own, or one that you have downloaded from GitHub.
## Attention developers and hobby hackers
<img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/hobby-programmer.svg' align='left' style='background-color:whitesmoke; width:80px; margin-right:15px; margin-bottom:10px;'/>
If you want to modify scripts, I recommend moving them to the `Excalidraw Automate script folder` or a different subfolder under the script folder. Scripts in the `Downloaded` folder will be overwritten when you click the `Update this script` button. Note also, that at this time, I do not check if the script file has been updated on GitHub, thus the `Update this script` button is always visible once you have installed a script, not only when an update is availble (hope to build this feature in the future).
I would love to include your contribution in the script library. If you have a script of your own that you would like to share with the community, please open a [PR](https://github.com/zsviczian/obsidian-excalidraw-plugin/pulls) on GitHub. Be sure to include the following in your pull request
- The [script file](https://github.com/zsviczian/obsidian-excalidraw-plugin/tree/master/ea-scripts) with a self explanetory name. The name of the file will be the name of the script in the Command Palette.
- An [image](https://github.com/zsviczian/obsidian-excalidraw-plugin/tree/master/images) explaining the scripts purpose. Remember a picture speaks thousand words!
- An update to this file [ea-scripts/index.md](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/index.md)
---
# List of available scripts
- [[#Box Selected Elements]]
- [[#Bullet Point]]
- [[#Connect elements]]
- [[#Convert text to link with folder and alias]]
- [[#Create new markdown file and embed into active drawing]]
- [[#Darken background color]]
- [[#Dimensions]]
- [[#Elbow connectors]]
- [[#Expand rectangles horizontally keep text centered]]
- [[#Expand rectangles horizontally]]
- [[#Expand rectangles vertically keep text centered]]
- [[#Expand rectangles vertically]]
- [[#Fixed spacing]]
- [[#Fixed vertical distance]]
- [[#Font Family]]
- [[#Grid]]
- [[#Lighten background color]]
- [[#Modify background color opacity]]
- [[#Modify stroke width of selected elements]]
- [[#OCR - Optical Character Recognition]]
- [[#Reverse arrows]]
- [[#Set Link Alias]]
- [[#Split text by lines]]
- [[#Text Align]]
- [[#Transfer TextElements to Excalidraw markdown metadata]]
- [[#Zoom to Fit Selected Elements]]
## Box Selected Elements
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Box%20Selected%20Elements.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Box%20Selected%20Elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will add an encapsulating box around the currently selected elements in Excalidraw.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-elements.jpg'></td></tr></table>
## Bullet Point
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Bullet%20Point.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Bullet%20Point.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will add a small circle to the top left of each text element in the selection and add the text and the "bullet point" into a group.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-bullet-point.jpg'></td></tr></table>
## Connect elements
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Connect%20elements.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Connect%20elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will connect two objects with an arrow. If either of the objects are a set of grouped elements (e.g. a text element grouped with an encapsulating rectangle), the script will identify these groups, and connect the arrow to the largest object in the group (assuming you want to connect the arrow to the box around the text element).<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-connect-elements.jpg'></td></tr></table>
## Convert text to link with folder and alias
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20text%20to%20link%20with%20folder%20and%20alias.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Convert%20text%20to%20link%20with%20folder%20and%20alias.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Converts text elements to links pointing to a file in a selected folder and with the alias set as the original text. The script will prompt the user to select an existing folder from the vault.<br><code>original text</code> - <code>[[selected folder/original text|original text]]</code></td></tr></table>
## Create new markdown file and embed into active drawing
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Create%20new%20markdown%20file%20and%20embed%20into%20active%20drawing.md
```
<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/Create%20new%20markdown%20file%20and%20embed%20into%20active%20drawing.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script will prompt you for a filename, then create a new markdown document with the file name provided, open the new markdown document in an adjacent pane, and embed the markdown document into the active Excalidraw drawing.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-create-and-embed-new-markdown-file.jpg'></td></tr></table>
## Darken background color
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Darken%20background%20color.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Darken%20background%20color.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script darkens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect. In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png'></td></tr></table>
## Dimensions
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Dimensions.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Dimensions.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Currently there is no way to specify the exact location and size of objects in Excalidraw. You can bridge this gap with the following simple script.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-dimensions.jpg'></td></tr></table>
## Elbow connectors
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Elbow%20connectors.md
```
<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/Elbow%20connectors.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script converts the selected connectors to elbows.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/elbow-connectors.png'></td></tr></table>
## Expand rectangles horizontally keep text centered
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20horizontally%20keep%20text%20centered.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Expand%20rectangles%20horizontally%20keep%20text%20centered.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script expands the width of the selected rectangles until they are all the same width and keep the text centered.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif'></td></tr></table>
## Expand rectangles horizontally
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20horizontally.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Expand%20rectangles%20horizontally.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script expands the width of the selected rectangles until they are all the same width.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif'></td></tr></table>
## Expand rectangles vertically keep text centered
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20vertically%20keep%20text%20centered.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Expand%20rectangles%20vertically%20keep%20text%20centered.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script expands the height of the selected rectangles until they are all the same height and keep the text centered.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif'></td></tr></table>
## Expand rectangles vertically
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20vertically.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Expand%20rectangles%20vertically.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script expands the height of the selected rectangles until they are all the same height.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-expand-rectangles.gif'></td></tr></table>
## Fixed spacing
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20spacing.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Fixed%20spacing.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script arranges the selected elements horizontally with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fix-space-demo.png'></td></tr></table>
## Fixed vertical distance
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20vertical%20distance.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Fixed%20vertical%20distance.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script arranges the selected elements vertically with a fixed spacing. When we create an architecture diagram or mind map, we often need to arrange a large number of elements in a fixed spacing. `Fixed spacing` and `Fixed vertical Distance` scripts can save us a lot of time.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-fixed-vertical-distance.png'></td></tr></table>
## Font Family
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Font%20Family.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Font%20Family.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Sets font family of the text block (Virgil, Helvetica, Cascadia). Useful if you want to set a keyboard shortcut for selecting font family.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-font-family.jpg'></td></tr></table>
## Grid
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Grid.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Grid.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The default grid size in Excalidraw is 20. Currently there is no way to change the grid size via the user interface. This script offers a way to bridge this gap.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid.jpg'></td></tr></table>
## Lighten background color
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Lighten%20background%20color.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Lighten%20background%20color.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script lightens the background color of the selected element by 2% at a time. You can use this script several times until you are satisfied. It is recommended to set a shortcut key for this script so that you can quickly try to DARKEN and LIGHTEN the color effect.In contrast to the `Modify background color opacity` script, the advantage is that the background color of the element is not affected by the canvas color, and the color value does not appear in a strange rgba() form.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/darken-lighten-background-color.png'></td></tr></table>
## Modify background color opacity
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Modify%20background%20color%20opacity.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Modify%20background%20color%20opacity.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script changes the opacity of the background color of the selected boxes. The default background color in Excalidraw is so dark that the text is hard to read. You can lighten the color a bit by setting transparency. And you can tweak the transparency over and over again until you're happy with it. Although excalidraw has the opacity option in its native property Settings, it also changes the transparency of the border. Use this script to change only the opacity of the background color without affecting the border.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-modify-background-color-opacity.png'></td></tr></table>
## Modify stroke width of selected elements
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Modify%20stroke%20width%20of%20selected%20elements.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Modify%20stroke%20width%20of%20selected%20elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will set the stroke width of selected elements. This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-stroke-width.jpg'></td></tr></table>
## OCR - Optical Character Recognition
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/OCR%20-%20Optical%20Character%20Recognition.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/OCR%20-%20Optical%20Character%20Recognition.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">REQUIRES EXCALIDRAW 1.5.15<br>The script will 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and 2) will add the text to your drawing as a text element.<br><mark>⚠ Note that you will need to manually paste your token into the script after the first run! ⚠</mark><br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-ocr.jpg'><br><iframe width="560" height="315" src="https://www.youtube.com/embed/W2NMzR8s4eE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></td></tr></table>
## Reverse arrows
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Reverse%20arrows.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/Reverse%20arrows.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Reverse the direction of **arrows** within the scope of selected elements.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-reverse-arrow.jpg'></td></tr></table>
## Set Link Alias
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Link%20Alias.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20Link%20Alias.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Iterates all of the links in the selected TextElements and prompts the user to set or modify the alias for each link found.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-set-link-alias.jpg'></td></tr></table>
## Split text by lines
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Split%20text%20by%20lines.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Split%20text%20by%20lines.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Split lines of text into separate text elements for easier reorganization<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-split-lines.jpg'></td></tr></table>
## Text Align
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Text%20Align.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Text%20Align.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-align.jpg'></td></tr></table>
## Transfer TextElements to Excalidraw markdown metadata
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md
```
<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/Transfer%20TextElements%20to%20Excalidraw%20markdown%20metadata.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script will delete the selected text elements from the canvas and will copy the text from these text elements into the Excalidraw markdown file as metadata. This means, that the text will no longer be visible in the drawing, however you will be able to search for the text in Obsidian and find the drawing containing this image.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-to-metadata.jpg'></td></tr></table>
## Zoom to Fit Selected Elements
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Similar to Excalidraw standard SHIFT+2 feature: Zoom to fit selected elements, but with the ability to zoom to 1000%. Inspiration: [#272](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/272)</td></tr></table>

View File

@@ -1,3 +1,4 @@
{
"minify": true
"minifyWhitespace": true,
"minifySyntax":true
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
images/elbow-connectors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
images/scripts-grid.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
images/scripts-ocr.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

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

View File

@@ -1,43 +1,52 @@
{
"name": "obsidian-excalidraw-plugin",
"version": "1.0.4",
"version": "1.3.21",
"description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings",
"main": "main.js",
"scripts": {
"dev": "cross-env NODE_ENV=development rollup --config rollup.config.js -w",
"build": "cross-env NODE_ENV=production rollup --config rollup.config.js"
"build": "cross-env NODE_ENV=production rollup --config rollup.config.js",
"code:fix": "eslint --max-warnings=0 --ext .ts,.tsx ./src --fix"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@excalidraw/excalidraw": "0.7.0",
"aakansha-excalidraw": "0.7.0-draft",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-scripts": "4.0.1"
"@zsviczian/excalidraw": "0.10.0-obsidian-33",
"monkey-around": "^2.2.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "^1.1.5",
"roughjs": "4.4.1"
},
"devDependencies": {
"@babel/core": "^7.3.3",
"@babel/preset-env": "^7.3.1",
"@babel/preset-react": "^7.0.0",
"@rollup/plugin-babel": "5.3.0",
"@rollup/plugin-commonjs": "^15.1.0",
"@rollup/plugin-node-resolve": "^9.0.0",
"@rollup/plugin-typescript": "^6.0.0",
"@types/node": "^14.14.2",
"@types/react-dom": "^17.0.0",
"cross-env": "7.0.3",
"nanoid": "3.1.22",
"obsidian": "https://github.com/obsidianmd/obsidian-api/tarball/master",
"postcss": "^8.2.6",
"rollup": "2.45.2",
"rollup-plugin-copy": "3.4.0",
"rollup-plugin-minify": "1.0.3",
"rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-visualizer": "^5.4.1",
"tslib": "^2.0.3",
"typescript": "^4.0.3",
"webpack-bundle-analyzer": "^4.4.1"
}
"@babel/core": "^7.15.5",
"@babel/preset-env": "^7.15.6",
"@babel/preset-react": "^7.14.5",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^21.0.0",
"@rollup/plugin-node-resolve": "^13.0.5",
"@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.5",
"@types/js-beautify": "^1.13.3",
"@types/node": "^15.12.4",
"@types/react-dom": "^17.0.9",
"cross-env": "^7.0.3",
"html2canvas": "^1.3.2",
"nanoid": "^3.1.23",
"obsidian": "^0.13.11",
"rollup": "^2.52.3",
"rollup-plugin-visualizer": "^5.5.2",
"tslib": "^2.3.1",
"typescript": "^4.4.3",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-prettier": "3.3.1",
"prettier": "2.5.0",
"@excalidraw/eslint-config": "1.0.0",
"@excalidraw/prettier-config": "1.0.2"
},
"resolutions": {
"@typescript-eslint/typescript-estree": "5.3.0"
},
"prettier": "@excalidraw/prettier-config"
}

View File

@@ -1,7 +1,6 @@
import typescript from '@rollup/plugin-typescript';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
//import copy from 'rollup-plugin-copy';
import { env } from "process";
import babel from '@rollup/plugin-babel';
import replace from "@rollup/plugin-replace";

585
src/EmbeddedFileLoader.ts Normal file
View File

@@ -0,0 +1,585 @@
import { FileId } from "@zsviczian/excalidraw/types/element/types";
import { BinaryFileData, DataURL } from "@zsviczian/excalidraw/types/types";
import { App, MarkdownRenderer, Notice, TFile } from "obsidian";
import {
CASCADIA_FONT,
fileid,
FRONTMATTER_KEY_FONT,
FRONTMATTER_KEY_FONTCOLOR,
FRONTMATTER_KEY_MD_STYLE,
IMAGE_TYPES,
nanoid,
VIRGIL_FONT,
} from "./constants";
import { createSVG } from "./ExcalidrawAutomate";
import { ExcalidrawData, getTransclusion } from "./ExcalidrawData";
import { ExportSettings } from "./ExcalidrawView";
import { t } from "./lang/helpers";
import { tex2dataURL } from "./LaTeX";
import ExcalidrawPlugin from "./main";
import {
errorlog,
getImageSize,
getLinkParts,
LinkParts,
svgToBase64,
} from "./Utils";
export declare type MimeType =
| "image/svg+xml"
| "image/png"
| "image/jpeg"
| "image/gif"
| "application/octet-stream";
export type FileData = BinaryFileData & {
size: Size;
hasSVGwithBitmap: boolean;
};
export type Size = {
height: number;
width: number;
};
export class EmbeddedFile {
public file: TFile = null;
public isSVGwithBitmap: boolean = false;
private img: string = ""; //base64
private imgInverted: string = ""; //base64
public mtime: number = 0; //modified time of the image
private plugin: ExcalidrawPlugin;
public mimeType: MimeType = "application/octet-stream";
public size: Size = { height: 0, width: 0 };
public linkParts: LinkParts;
constructor(plugin: ExcalidrawPlugin, hostPath: string, imgPath: string) {
this.plugin = plugin;
this.resetImage(hostPath, imgPath);
}
public resetImage(hostPath: string, imgPath: string) {
this.imgInverted = this.img = "";
this.mtime = 0;
this.linkParts = getLinkParts(imgPath);
if (!this.linkParts.path) {
new Notice(`Excalidraw Error\nIncorrect embedded filename: ${imgPath}`);
return;
}
if (!this.linkParts.width) {
this.linkParts.width = this.plugin.settings.mdSVGwidth;
}
if (!this.linkParts.height) {
this.linkParts.height = this.plugin.settings.mdSVGmaxHeight;
}
this.file = this.plugin.app.metadataCache.getFirstLinkpathDest(
this.linkParts.path,
hostPath,
);
if (!this.file) {
new Notice(
`Excalidraw Warning: could not find image file: ${imgPath}`,
5000,
);
}
}
private fileChanged(): boolean {
if (!this.file) {
return false;
}
return this.mtime != this.file.stat.mtime;
}
public setImage(
imgBase64: string,
mimeType: MimeType,
size: Size,
isDark: boolean,
isSVGwithBitmap: boolean,
) {
if (!this.file) {
return;
}
if (this.fileChanged()) {
this.imgInverted = this.img = "";
}
this.mtime = this.file.stat.mtime;
this.size = size;
this.mimeType = mimeType;
switch (isDark && isSVGwithBitmap) {
case true:
this.imgInverted = imgBase64;
break;
case false:
this.img = imgBase64;
break; //bitmaps and SVGs without an embedded bitmap do not need a negative image
}
this.isSVGwithBitmap = isSVGwithBitmap;
}
public isLoaded(isDark: boolean): boolean {
if (!this.file) {
return true;
}
if (this.fileChanged()) {
return false;
}
if (this.isSVGwithBitmap && isDark) {
return this.imgInverted !== "";
}
return this.img !== "";
}
public getImage(isDark: boolean) {
if (!this.file) {
return "";
}
if (isDark && this.isSVGwithBitmap) {
return this.imgInverted;
}
return this.img; //images that are not SVGwithBitmap, only the light string is stored, since inverted and non-inverted are ===
}
}
export class EmbeddedFilesLoader {
private plugin: ExcalidrawPlugin;
private processedFiles: Map<string, number> = new Map<string, number>();
private isDark: boolean;
public terminate = false;
public uid: string;
constructor(plugin: ExcalidrawPlugin, isDark?: boolean) {
this.plugin = plugin;
this.isDark = isDark;
this.uid = nanoid();
}
public async getObsidianImage(inFile: TFile | EmbeddedFile): Promise<{
mimeType: MimeType;
fileId: FileId;
dataURL: DataURL;
created: number;
hasSVGwithBitmap: boolean;
size: { height: number; width: number };
}> {
if (!this.plugin || !inFile) {
return null;
}
const file: TFile = inFile instanceof EmbeddedFile ? inFile.file : inFile;
const linkParts =
inFile instanceof EmbeddedFile
? inFile.linkParts
: {
original: file.path,
path: file.path,
isBlockRef: false,
ref: null,
width: this.plugin.settings.mdSVGwidth,
height: this.plugin.settings.mdSVGmaxHeight,
};
//to block infinite loop of recursive loading of images
const count = this.processedFiles.has(file.path)
? this.processedFiles.get(file.path)
: 0;
if (file.extension === "md" && count > 2) {
new Notice(t("INFINITE_LOOP_WARNING") + file.path, 6000);
return null;
}
this.processedFiles.set(file.path, count + 1);
let hasSVGwithBitmap = false;
const app = this.plugin.app;
const isExcalidrawFile = this.plugin.isExcalidrawFile(file);
if (
!(
IMAGE_TYPES.contains(file.extension) ||
isExcalidrawFile ||
file.extension === "md"
)
) {
return null;
}
const ab = await app.vault.readBinary(file);
const getExcalidrawSVG = async (isDark: boolean) => {
//debug({where:"EmbeddedFileLoader.getExcalidrawSVG",uid:this.uid,file:file.name});
const exportSettings: ExportSettings = {
withBackground: false,
withTheme: false,
};
const svg = await createSVG(
file.path,
true,
exportSettings,
this,
null,
null,
null,
[],
this.plugin,
);
//https://stackoverflow.com/questions/51154171/remove-css-filter-on-child-elements
const imageList = svg.querySelectorAll(
"image:not([href^='data:image/svg'])",
);
if (imageList.length > 0) {
hasSVGwithBitmap = true;
}
if (hasSVGwithBitmap && isDark) {
const THEME_FILTER = "invert(100%) hue-rotate(180deg) saturate(1.25)";
imageList.forEach((i) => {
const id = i.parentElement?.id;
svg.querySelectorAll(`use[href='#${id}']`).forEach((u) => {
u.setAttribute("filter", THEME_FILTER);
});
});
}
if (!hasSVGwithBitmap && svg.getAttribute("hasbitmap")) {
hasSVGwithBitmap = true;
}
const dURL = svgToBase64(svg.outerHTML) as DataURL;
return dURL as DataURL;
};
const excalidrawSVG = isExcalidrawFile
? await getExcalidrawSVG(this.isDark)
: null;
let mimeType: MimeType = "image/svg+xml";
if (!isExcalidrawFile) {
switch (file.extension) {
case "png":
mimeType = "image/png";
break;
case "jpeg":
mimeType = "image/jpeg";
break;
case "jpg":
mimeType = "image/jpeg";
break;
case "gif":
mimeType = "image/gif";
break;
case "svg":
case "md":
mimeType = "image/svg+xml";
break;
default:
mimeType = "application/octet-stream";
}
}
const dataURL =
excalidrawSVG ??
(file.extension === "svg"
? await getSVGData(app, file)
: file.extension === "md"
? await convertMarkdownToSVG(this.plugin, file, linkParts)
: await getDataURL(ab, mimeType));
const size = await getImageSize(
excalidrawSVG ??
(file.extension === "md" ? dataURL : app.vault.getResourcePath(file)),
);
return {
mimeType,
fileId: await generateIdFromFile(ab),
dataURL,
created: file.stat.mtime,
hasSVGwithBitmap,
size,
};
}
public async loadSceneFiles(
excalidrawData: ExcalidrawData,
addFiles: Function,
) {
const entries = excalidrawData.getFileEntries();
//debug({where:"EmbeddedFileLoader.loadSceneFiles",uid:this.uid,isDark:this.isDark,sceneTheme:excalidrawData.scene.appState.theme});
if (this.isDark === undefined) {
this.isDark = excalidrawData.scene.appState.theme === "dark";
}
let entry;
const files: FileData[] = [];
while (!this.terminate && !(entry = entries.next()).done) {
const embeddedFile: EmbeddedFile = entry.value[1];
if (!embeddedFile.isLoaded(this.isDark)) {
//debug({where:"EmbeddedFileLoader.loadSceneFiles",uid:this.uid,status:"embedded Files are not loaded"});
const data = await this.getObsidianImage(embeddedFile);
if (data) {
files.push({
mimeType: data.mimeType,
id: entry.value[0],
dataURL: data.dataURL,
created: data.created,
size: data.size,
hasSVGwithBitmap: data.hasSVGwithBitmap,
});
}
} else if (embeddedFile.isSVGwithBitmap) {
files.push({
mimeType: embeddedFile.mimeType,
id: entry.value[0],
dataURL: embeddedFile.getImage(this.isDark) as DataURL,
created: embeddedFile.mtime,
size: embeddedFile.size,
hasSVGwithBitmap: embeddedFile.isSVGwithBitmap,
});
}
}
let equation;
const equations = excalidrawData.getEquationEntries();
while (!this.terminate && !(equation = equations.next()).done) {
if (!excalidrawData.getEquation(equation.value[0]).isLoaded) {
const latex = equation.value[1].latex;
const data = await tex2dataURL(latex, this.plugin);
if (data) {
files.push({
mimeType: data.mimeType,
id: equation.value[0],
dataURL: data.dataURL,
created: data.created,
size: data.size,
hasSVGwithBitmap: false,
});
}
}
}
if (this.terminate) {
return;
}
//debug({where:"EmbeddedFileLoader.loadSceneFiles",uid:this.uid,status:"add Files"});
try {
//in try block because by the time files are loaded the user may have closed the view
addFiles(files, this.isDark);
} catch (e) {
errorlog({ where: "EmbeddedFileLoader.loadSceneFiles", error: e });
}
}
}
const getSVGData = async (app: App, file: TFile): Promise<DataURL> => {
const svg = await app.vault.read(file);
return svgToBase64(svg) as DataURL;
};
const convertMarkdownToSVG = async (
plugin: ExcalidrawPlugin,
file: TFile,
linkParts: LinkParts,
): Promise<DataURL> => {
//1.
//get the markdown text
let text = (await getTransclusion(linkParts, plugin.app, file)).contents;
if (text === "") {
text =
"# Empty markdown file\nCTRL+Click here to open the file for editing in the current active pane, or CTRL+SHIFT+Click to open it in an adjacent pane.";
}
//2.
//get styles
const fileCache = plugin.app.metadataCache.getFileCache(file);
let fontDef: string;
let fontName = plugin.settings.mdFont;
if (
fileCache?.frontmatter &&
fileCache.frontmatter[FRONTMATTER_KEY_FONT] != null
) {
fontName = fileCache.frontmatter[FRONTMATTER_KEY_FONT];
}
switch (fontName) {
case "Virgil":
fontDef = VIRGIL_FONT;
break;
case "Cascadia":
fontDef = CASCADIA_FONT;
break;
case "":
fontDef = "";
break;
default:
const f = plugin.app.metadataCache.getFirstLinkpathDest(
fontName,
file.path,
);
if (f) {
const ab = await plugin.app.vault.readBinary(f);
const mimeType = f.extension.startsWith("woff")
? "application/font-woff"
: "font/truetype";
fontName = f.basename;
fontDef = ` @font-face {font-family: "${fontName}";src: url("${await getDataURL(
ab,
mimeType,
)}") format("${f.extension === "ttf" ? "truetype" : f.extension}");}`;
const split = fontDef.split(";base64,", 2);
fontDef = `${split[0]};charset=utf-8;base64,${split[1]}`;
} else {
fontDef = "";
}
}
const fontColor = fileCache?.frontmatter
? fileCache.frontmatter[FRONTMATTER_KEY_FONTCOLOR] ??
plugin.settings.mdFontColor
: plugin.settings.mdFontColor;
let style = fileCache?.frontmatter
? fileCache.frontmatter[FRONTMATTER_KEY_MD_STYLE] ?? ""
: "";
let frontmatterCSSisAfile = false;
if (style && style != "") {
const f = plugin.app.metadataCache.getFirstLinkpathDest(style, file.path);
if (f) {
style = await plugin.app.vault.read(f);
frontmatterCSSisAfile = true;
}
}
if (
!frontmatterCSSisAfile &&
plugin.settings.mdCSS &&
plugin.settings.mdCSS != ""
) {
const f = plugin.app.metadataCache.getFirstLinkpathDest(
plugin.settings.mdCSS,
file.path,
);
if (f) {
style += `\n${await plugin.app.vault.read(f)}`;
}
}
//3.
//SVG helper functions
//the SVG will first have ~infinite height. After sizing this will be reduced
let svgStyle = ` width="${linkParts.width}px" height="100000"`;
let foreignObjectStyle = ` width="${linkParts.width}px" height="100%"`;
const svg = (xml: string, xmlFooter: string, style?: string) =>
`<svg xmlns="http://www.w3.org/2000/svg"${svgStyle}>${
style ? `<style>${style}</style>` : ""
}<foreignObject x="0" y="0"${foreignObjectStyle}>${xml}${
xmlFooter //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/286#issuecomment-982179639
}</foreignObject>${
fontDef !== "" ? `<defs><style>${fontDef}</style></defs>` : ""
}</svg>`;
//4.
//create document div - this will be the contents of the foreign object
const mdDIV = createDiv();
mdDIV.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
mdDIV.setAttribute("class", "excalidraw-md-host");
// mdDIV.setAttribute("style",style);
if (fontName !== "") {
mdDIV.style.fontFamily = fontName;
}
mdDIV.style.overflow = "auto";
mdDIV.style.display = "block";
if (fontColor && fontColor != "") {
mdDIV.style.color = fontColor;
}
await MarkdownRenderer.renderMarkdown(text, mdDIV, file.path, plugin);
mdDIV
.querySelectorAll(":scope > *[class^='frontmatter']")
.forEach((el) => mdDIV.removeChild(el));
//5.1
//get SVG size.
//First I need to create a fully self contained copy of the document to convert
//blank styles into inline styles using computedStyle
const iframeHost = document.body.createDiv();
iframeHost.style.display = "none";
const iframe = iframeHost.createEl("iframe");
const iframeDoc = iframe.contentWindow.document;
if (style) {
const styleEl = iframeDoc.createElement("style");
styleEl.type = "text/css";
styleEl.innerHTML = style;
iframeDoc.head.appendChild(styleEl);
}
const stylingDIV = iframeDoc.importNode(mdDIV, true);
iframeDoc.body.appendChild(stylingDIV);
const footerDIV = createDiv();
footerDIV.setAttribute("class", "excalidraw-md-footer");
iframeDoc.body.appendChild(footerDIV);
iframeDoc.body.querySelectorAll("*").forEach((el: HTMLElement) => {
const elementStyle = el.style;
const computedStyle = window.getComputedStyle(el);
let style = "";
for (const prop in elementStyle) {
if (elementStyle.hasOwnProperty(prop)) {
style += `${prop}: ${computedStyle[prop]};`;
}
}
el.setAttribute("style", style);
});
const xmlINiframe = new XMLSerializer().serializeToString(stylingDIV);
const xmlFooter = new XMLSerializer().serializeToString(footerDIV);
document.body.removeChild(iframeHost);
//5.2
//get SVG size
const parser = new DOMParser();
const doc = parser.parseFromString(
svg(xmlINiframe, xmlFooter),
"image/svg+xml",
);
const svgEl = doc.firstElementChild;
const host = createDiv();
host.appendChild(svgEl);
document.body.appendChild(host);
const footerHeight = svgEl.querySelector(
".excalidraw-md-footer",
).scrollHeight;
const height =
svgEl.querySelector(".excalidraw-md-host").scrollHeight + footerHeight;
const svgHeight = height <= linkParts.height ? height : linkParts.height;
document.body.removeChild(host);
//finalize SVG
svgStyle = ` width="${linkParts.width}px" height="${svgHeight}px"`;
foreignObjectStyle = ` width="${linkParts.width}px" height="${svgHeight}px"`;
mdDIV.style.height = `${svgHeight - footerHeight}px`;
mdDIV.style.overflow = "hidden";
const xml = new XMLSerializer().serializeToString(mdDIV);
const finalSVG = svg(xml, '<div class="excalidraw-md-footer"></div>', style);
plugin.ea.mostRecentMarkdownSVG = parser.parseFromString(
finalSVG,
"image/svg+xml",
).firstElementChild as SVGSVGElement;
return svgToBase64(finalSVG) as DataURL;
};
const getDataURL = async (
file: ArrayBuffer,
mimeType: string,
): Promise<DataURL> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const dataURL = reader.result as DataURL;
resolve(dataURL);
};
reader.onerror = (error) => reject(error);
reader.readAsDataURL(new Blob([new Uint8Array(file)], { type: mimeType }));
});
};
const generateIdFromFile = async (file: ArrayBuffer): Promise<FileId> => {
let id: FileId;
try {
const hashBuffer = await window.crypto.subtle.digest("SHA-1", file);
id =
// convert buffer to byte array
Array.from(new Uint8Array(hashBuffer))
// convert to hex string
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("") as FileId;
} catch (error) {
errorlog({ where: "EmbeddedFileLoader.generateIdFromFile", error });
id = fileid() as FileId;
}
return id;
};

1616
src/ExcalidrawAutomate.ts Normal file

File diff suppressed because it is too large Load Diff

1140
src/ExcalidrawData.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,470 +0,0 @@
import ExcalidrawPlugin from "./main";
import {
ExcalidrawElement,
FillStyle,
StrokeStyle,
StrokeSharpness,
FontFamily,
} from "@excalidraw/excalidraw/types/element/types";
import {nanoid} from "nanoid";
import {
normalizePath,
parseFrontMatterAliases,
TFile
} from "obsidian"
import ExcalidrawView from "./ExcalidrawView"
declare type ConnectionPoint = "top"|"bottom"|"left"|"right";
export interface ExcalidrawAutomate extends Window {
ExcalidrawAutomate: {
plugin: ExcalidrawPlugin;
elementIds: [];
elementsDict: {},
style: {
strokeColor: string;
backgroundColor: string;
angle: number;
fillStyle: FillStyle;
strokeWidth: number;
storkeStyle: StrokeStyle;
roughness: number;
opacity: number;
strokeSharpness: StrokeSharpness;
fontFamily: FontFamily;
fontSize: number;
textAlign: string;
verticalAlign: string;
startArrowHead: string;
endArrowHead: string;
}
canvas: {theme: string, viewBackgroundColor: string};
setFillStyle: Function;
setStrokeStyle: Function;
setStrokeSharpness: Function;
setFontFamily: Function;
setTheme: Function;
addRect: Function;
addDiamond: Function;
addEllipse: Function;
addText: Function;
addLine: Function;
addArrow: Function;
connectObjects: Function;
addToGroup: Function;
toClipboard: Function;
create: Function;
createPNG: Function;
createSVG: Function;
clear: Function;
reset: Function;
};
}
declare let window: ExcalidrawAutomate;
export function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
window.ExcalidrawAutomate = {
plugin: plugin,
elementIds: [],
elementsDict: {},
style: {
strokeColor: "#000000",
backgroundColor: "transparent",
angle: 0,
fillStyle: "hachure",
strokeWidth:1,
storkeStyle: "solid",
roughness: 1,
opacity: 100,
strokeSharpness: "sharp",
fontFamily: 1,
fontSize: 20,
textAlign: "left",
verticalAlign: "top",
startArrowHead: null,
endArrowHead: "arrow"
},
canvas: {theme: "light", viewBackgroundColor: "#FFFFFF"},
setFillStyle (val:number) {
switch(val) {
case 0:
this.style.fillStyle = "hachure";
return "hachure";
case 1:
this.style.fillStyle = "cross-hatch";
return "cross-hatch";
default:
this.style.fillStyle = "solid";
return "solid";
}
},
setStrokeStyle (val:number) {
switch(val) {
case 0:
this.style.strokeStyle = "solid";
return "solid";
case 1:
this.style.strokeStyle = "dashed";
return "dashed";
default:
this.style.strokeStyle = "dotted";
return "dotted";
}
},
setStrokeSharpness (val:number) {
switch(val) {
case 0:
this.style.strokeSharpness = "round";
return "round";
default:
this.style.strokeSharpness = "sharp";
return "sharp";
}
},
setFontFamily (val:number) {
switch(val) {
case 1:
this.style.fontFamily = 1;
return getFontFamily(1);
case 2:
this.style.fontFamily = 2;
return getFontFamily(2);
default:
this.style.strokeSharpness = 3;
return getFontFamily(3);
}
},
setTheme (val:number) {
switch(val) {
case 0:
this.canvas.theme = "light";
return "light";
default:
this.canvas = "dark";
return "dark";
}
},
addToGroup(objectIds:[]):void {
const id = nanoid();
objectIds.forEach((objectId)=>{
this.elementsDict[objectId]?.groupIds?.push(id);
});
},
async toClipboard(templatePath?:string) {
let elements = templatePath ? (await getTemplate(templatePath)).elements : [];
for (let i=0;i<this.elementIds.length;i++) {
elements.push(this.elementsDict[this.elementIds[i]]);
}
navigator.clipboard.writeText(
JSON.stringify({
"type":"excalidraw/clipboard",
"elements": elements,
}));
},
async create(params?:{filename: string, foldername:string, templatePath:string, onNewPane: boolean}) {
let elements = params?.templatePath ? (await getTemplate(params.templatePath)).elements : [];
for (let i=0;i<this.elementIds.length;i++) {
elements.push(this.elementsDict[this.elementIds[i]]);
}
plugin.createDrawing(
params?.filename ? params.filename + '.excalidraw' : this.plugin.getNextDefaultFilename(),
params?.onNewPane ? params.onNewPane : false,
params?.foldername ? params.foldername : this.plugin.settings.folder,
JSON.stringify({
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": elements,
"appState": {
"theme": this.canvas.theme,
"viewBackgroundColor": this.canvas.viewBackgroundColor
}
})
);
},
async createSVG(templatePath?:string) {
let elements = templatePath ? (await getTemplate(templatePath)).elements : [];
for (let i=0;i<this.elementIds.length;i++) {
elements.push(this.elementsDict[this.elementIds[i]]);
}
return ExcalidrawView.getSVG(
JSON.stringify({
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": elements,
"appState": {
"theme": this.canvas.theme,
"viewBackgroundColor": this.canvas.viewBackgroundColor
}
}),
{
withBackground: plugin.settings.exportWithBackground,
withTheme: plugin.settings.exportWithTheme
}
)
},
async createPNG(templatePath?:string) {
let elements = templatePath ? (await getTemplate(templatePath)).elements : [];
for (let i=0;i<this.elementIds.length;i++) {
elements.push(this.elementsDict[this.elementIds[i]]);
}
return ExcalidrawView.getPNG(
JSON.stringify({
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": elements,
"appState": {
"theme": this.canvas.theme,
"viewBackgroundColor": this.canvas.viewBackgroundColor
}
}),
{
withBackground: plugin.settings.exportWithBackground,
withTheme: plugin.settings.exportWithTheme
}
)
},
addRect(topX:number, topY:number, width:number, height:number):string {
const id = nanoid();
this.elementIds.push(id);
this.elementsDict[id] = boxedElement(id,"rectangle",topX,topY,width,height);
return id;
},
addDiamond(topX:number, topY:number, width:number, height:number):string {
const id = nanoid();
this.elementIds.push(id);
this.elementsDict[id] = boxedElement(id,"diamond",topX,topY,width,height);
return id;
},
addEllipse(topX:number, topY:number, width:number, height:number):string {
const id = nanoid();
this.elementIds.push(id);
this.elementsDict[id] = boxedElement(id,"ellipse",topX,topY,width,height);
return id;
},
addText(topX:number, topY:number, text:string, formatting?:{width:number, height:number,textAlign: string, verticalAlign:string, box: boolean, boxPadding: number}):string {
const id = nanoid();
const {w, h, baseline} = measureText(text);
const width = formatting?.width ? formatting.width : w;
const height = formatting?.height ? formatting.height : h;
this.elementIds.push(id);
this.elementsDict[id] = {
text: text,
fontSize: window.ExcalidrawAutomate.style.fontSize,
fontFamily: window.ExcalidrawAutomate.style.fontFamily,
textAlign: formatting?.textAlign ? formatting.textAlign : window.ExcalidrawAutomate.style.textAlign,
verticalAlign: formatting?.verticalAlign ? formatting.verticalAlign : window.ExcalidrawAutomate.style.verticalAlign,
baseline: baseline,
... boxedElement(id,"text",topX,topY,width,height)
};
if(formatting?.box) {
const boxPadding = formatting?.boxPadding ? formatting.boxPadding : 10;
const boxId = this.addRect(topX-boxPadding,topY-boxPadding,width+2*boxPadding,height+2*boxPadding);
this.addToGroup([id,boxId])
return boxId;
}
return id;
},
addLine(points: [[x:number,y:number]]):void {
const box = getLineBox(points);
const id = nanoid();
this.elementIds.push(id);
this.elementsDict[id] = {
points: normalizeLinePoints(points),
lastCommittedPoint: null,
startBinding: null,
endBinding: null,
startArrowhead: null,
endArrowhead: null,
... boxedElement(id,"line",box.x,box.y,box.w,box.h)
};
},
addArrow(points: [[x:number,y:number]],formatting?:{startArrowHead:string,endArrowHead:string,startObjectId:string,endObjectId:string}):void {
const box = getLineBox(points);
const id = nanoid();
this.elementIds.push(id);
this.elementsDict[id] = {
points: normalizeLinePoints(points),
lastCommittedPoint: null,
startBinding: {elementId:formatting?.startObjectId,focus:0.1,gap:4},
endBinding: {elementId:formatting?.endObjectId,focus:0.1,gap:4},
startArrowhead: formatting?.startArrowHead ? formatting.startArrowHead : this.style.startArrowHead,
endArrowhead: formatting?.endArrowHead ? formatting.endArrowHead : this.style.endArrowHead,
... boxedElement(id,"arrow",box.x,box.y,box.w,box.h)
};
if(formatting?.startObjectId) this.elementsDict[formatting.startObjectId].boundElementIds.push(id);
if(formatting?.endObjectId) this.elementsDict[formatting.endObjectId].boundElementIds.push(id);
},
connectObjects(objectA: string, connectionA: ConnectionPoint, objectB: string, connectionB: ConnectionPoint, formatting?:{numberOfPoints: number,startArrowHead:string,endArrowHead:string, padding: number}):void {
if(!(this.elementsDict[objectA] && this.elementsDict[objectB])) {
return;
}
const padding = formatting?.padding ? formatting.padding : 10;
const numberOfPoints = formatting?.numberOfPoints ? formatting.numberOfPoints : 0;
const getSidePoints = (side:string, el:any) => {
switch(side) {
case "bottom":
return [((el.x) + (el.x+el.width))/2, el.y+el.height+padding];
case "left":
return [el.x-padding, ((el.y) + (el.y+el.height))/2];
case "right":
return [el.x+el.width+padding, ((el.y) + (el.y+el.height))/2];
default: //"top"
return [((el.x) + (el.x+el.width))/2, el.y-padding];
}
}
const [aX, aY] = getSidePoints(connectionA,this.elementsDict[objectA]);
const [bX, bY] = getSidePoints(connectionB,this.elementsDict[objectB]);
const numAP = numberOfPoints+2; //number of break points plus the beginning and the end
let points = [];
for(let i=0;i<numAP;i++)
points.push([aX+i*(bX-aX)/(numAP-1), aY+i*(bY-aY)/(numAP-1)]);
this.addArrow(points,{
startArrowHead: formatting?.startArrowHead,
endArrowHead: formatting?.endArrowHead,
startObjectId: objectA,
endObjectId: objectB
});
},
clear() {
this.elementIds = [];
this.elementsDict = {};
},
reset() {
this.clear();
this.style.strokeColor= "#000000";
this.style.backgroundColor= "transparent";
this.style.angle= 0;
this.style.fillStyle= "hachure";
this.style.strokeWidth= 1;
this.style.storkeStyle= "solid";
this.style.roughness= 1;
this.style.opacity= 100;
this.style.strokeSharpness= "sharp";
this.style.fontFamily= 1;
this.style.fontSize= 20;
this.style.textAlign= "left";
this.style.verticalAlign= "top";
this.style.startArrowHead= null;
this.style.endArrowHead= "arrow";
this.canvas.theme = "light";
this.canvas.viewBackgroundColor="#FFFFFF";
},
};
initFonts();
}
export function destroyExcalidrawAutomate() {
delete window.ExcalidrawAutomate;
}
function normalizeLinePoints(points:[[x:number,y:number]]) {
let p = [];
for(let i=0;i<points.length;i++) {
p.push([points[i][0]-points[0][0], points[i][1]-points[0][1]]);
}
return p;
}
function boxedElement(id:string,eltype:any,x:number,y:number,w:number,h:number) {
return {
id: id,
type: eltype,
x: x,
y: y,
width: w,
height: h,
angle: window.ExcalidrawAutomate.style.angle,
strokeColor: window.ExcalidrawAutomate.style.strokeColor,
backgroundColor: window.ExcalidrawAutomate.style.backgroundColor,
fillStyle: window.ExcalidrawAutomate.style.fillStyle,
strokeWidth: window.ExcalidrawAutomate.style.strokeWidth,
storkeStyle: window.ExcalidrawAutomate.style.storkeStyle,
roughness: window.ExcalidrawAutomate.style.roughness,
opacity: window.ExcalidrawAutomate.style.opacity,
strokeSharpness: window.ExcalidrawAutomate.style.strokeSharpness,
seed: Math.floor(Math.random() * 100000),
version: 1,
versionNounce: 1,
isDeleted: false,
groupIds: [] as any,
boundElementIds: [] as any,
};
}
function getLineBox(points: [[x:number,y:number]]) {
return {
x: points[0][0],
y: points[0][1],
w: Math.abs(points[points.length-1][0]-points[0][0]),
h: Math.abs(points[points.length-1][1]-points[0][1])
}
}
function getFontFamily(id:number) {
switch (id) {
case 1: return "Virgil, Segoe UI Emoji";
case 2: return "Helvetica, Segoe UI Emoji";
case 3: return "Cascadia, Segoe UI Emoji";
}
}
async function initFonts () {
for (let i=0;i<3;i++) {
await (document as any).fonts.load(
window.ExcalidrawAutomate.style.fontSize.toString()+'px ' +
getFontFamily(window.ExcalidrawAutomate.style.fontFamily)
);
}
}
function measureText (newText:string) {
const line = document.createElement("div");
const body = document.body;
line.style.position = "absolute";
line.style.whiteSpace = "pre";
line.style.font = window.ExcalidrawAutomate.style.fontSize.toString()+'px ' +
getFontFamily(window.ExcalidrawAutomate.style.fontFamily);
// await (document as any).fonts.load(line.style.font);
body.appendChild(line);
line.innerText = newText
.split("\n")
// replace empty lines with single space because leading/trailing empty
// lines would be stripped from computation
.map((x) => x || " ")
.join("\n");
const width = line.offsetWidth;
const height = line.offsetHeight;
// Now creating 1px sized item that will be aligned to baseline
// to calculate baseline shift
const span = document.createElement("span");
span.style.display = "inline-block";
span.style.overflow = "hidden";
span.style.width = "1px";
span.style.height = "1px";
line.appendChild(span);
// Baseline is important for positioning text on canvas
const baseline = span.offsetTop + span.offsetHeight;
document.body.removeChild(line);
return {w: width, h: height, baseline: baseline };
};
async function getTemplate(fileWithPath: string):Promise<{elements: any,appState: any}> {
const vault = window.ExcalidrawAutomate.plugin.app.vault;
const file = vault.getAbstractFileByPath(normalizePath(fileWithPath));
if(file && file instanceof TFile) {
const data = await vault.read(file);
const excalidrawData = JSON.parse(data);
return {
elements: excalidrawData.elements,
appState: excalidrawData.appState,
};
};
return {
elements: [],
appState: {},
}
}

File diff suppressed because it is too large Load Diff

53
src/InsertImageDialog.ts Normal file
View File

@@ -0,0 +1,53 @@
import { App, FuzzySuggestModal, TFile } from "obsidian";
import { IMAGE_TYPES } from "./constants";
import ExcalidrawView from "./ExcalidrawView";
import { t } from "./lang/helpers";
import ExcalidrawPlugin from "./main";
export class InsertImageDialog extends FuzzySuggestModal<TFile> {
public app: App;
public plugin: ExcalidrawPlugin;
private view: ExcalidrawView;
constructor(plugin: ExcalidrawPlugin) {
super(plugin.app);
this.plugin = plugin;
this.app = plugin.app;
this.limit = 20;
this.setInstructions([
{
command: t("SELECT_FILE"),
purpose: "",
},
]);
this.setPlaceholder(t("SELECT_DRAWING"));
this.emptyStateText = t("NO_MATCH");
}
getItems(): TFile[] {
return (this.app.vault.getFiles() || []).filter(
(f: TFile) =>
IMAGE_TYPES.contains(f.extension) || this.plugin.isExcalidrawFile(f),
);
}
getItemText(item: TFile): string {
return item.path;
}
onChooseItem(item: TFile): void {
const ea = this.plugin.ea;
ea.reset();
ea.setView(this.view);
ea.canvas.theme = this.view.excalidrawAPI.getAppState().theme;
(async () => {
await ea.addImage(0, 0, item);
ea.addElementsToView(true, false);
})();
}
public start(view: ExcalidrawView) {
this.view = view;
this.open();
}
}

49
src/InsertLinkDialog.ts Normal file
View File

@@ -0,0 +1,49 @@
import { App, FuzzySuggestModal, TFile } from "obsidian";
import { t } from "./lang/helpers";
export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
public app: App;
private addText: Function;
private drawingPath: string;
constructor(app: App) {
super(app);
this.app = app;
this.limit = 20;
this.setInstructions([
{
command: t("SELECT_FILE"),
purpose: "",
},
]);
this.setPlaceholder(t("SELECT_FILE_TO_LINK"));
this.emptyStateText = t("NO_MATCH");
}
getItems(): any[] {
//@ts-ignore
return this.app.metadataCache.getLinkSuggestions();
}
getItemText(item: any): string {
return item.path + (item.alias ? `|${item.alias}` : "");
}
onChooseItem(item: any): void {
let filepath = item.path;
if (item.file) {
filepath = this.app.metadataCache.fileToLinktext(
item.file,
this.drawingPath,
true,
);
}
this.addText(`[[${filepath + (item.alias ? `|${item.alias}` : "")}]]`);
}
public start(drawingPath: string, addText: Function) {
this.addText = addText;
this.drawingPath = drawingPath;
this.open();
}
}

50
src/InsertMDDialog.ts Normal file
View File

@@ -0,0 +1,50 @@
import { App, FuzzySuggestModal, TFile } from "obsidian";
import ExcalidrawView from "./ExcalidrawView";
import { t } from "./lang/helpers";
import ExcalidrawPlugin from "./main";
export class InsertMDDialog extends FuzzySuggestModal<TFile> {
public app: App;
public plugin: ExcalidrawPlugin;
private view: ExcalidrawView;
constructor(plugin: ExcalidrawPlugin) {
super(plugin.app);
this.plugin = plugin;
this.app = plugin.app;
this.limit = 20;
this.setInstructions([
{
command: t("SELECT_FILE"),
purpose: "",
},
]);
this.setPlaceholder(t("SELECT_MD"));
this.emptyStateText = t("NO_MATCH");
}
getItems(): TFile[] {
return (this.app.vault.getFiles() || []).filter(
(f: TFile) => f.extension === "md" && !this.plugin.isExcalidrawFile(f),
);
}
getItemText(item: TFile): string {
return item.path;
}
onChooseItem(item: TFile): void {
const ea = this.plugin.ea;
ea.reset();
ea.setView(this.view);
(async () => {
await ea.addImage(0, 0, item);
ea.addElementsToView(true, false);
})();
}
public start(view: ExcalidrawView) {
this.view = view;
this.open();
}
}

114
src/LaTeX.ts Normal file
View File

@@ -0,0 +1,114 @@
import { DataURL } from "@zsviczian/excalidraw/types/types";
import ExcalidrawView from "./ExcalidrawView";
import ExcalidrawPlugin from "./main";
import { FileData, MimeType } from "./EmbeddedFileLoader";
import { FileId } from "@zsviczian/excalidraw/types/element/types";
import { getImageSize, sleep, svgToBase64 } from "./Utils";
import { fileid } from "./constants";
import html2canvas from "html2canvas";
declare let window: any;
export const updateEquation = async (
equation: string,
fileId: string,
view: ExcalidrawView,
addFiles: Function,
plugin: ExcalidrawPlugin,
) => {
const data = await tex2dataURL(equation, plugin);
if (data) {
const files: FileData[] = [];
files.push({
mimeType: data.mimeType,
id: fileId as FileId,
dataURL: data.dataURL,
created: data.created,
size: data.size,
hasSVGwithBitmap: false,
});
addFiles(files, view);
}
};
export async function tex2dataURL(
tex: string,
plugin: ExcalidrawPlugin,
): Promise<{
mimeType: MimeType;
fileId: FileId;
dataURL: DataURL;
created: number;
size: { height: number; width: number };
}> {
//if network is slow, or not available, or mathjax has not yet fully loaded
try {
return await mathjaxSVG(tex, plugin);
} catch (e) {
await sleep(200); //grace period for mathjax to load, if not, then we go for the slower fallback
try {
return await mathjaxSVG(tex, plugin);
} catch (e) {
//fallback
return await mathjaxImage2html(tex);
}
}
}
async function mathjaxSVG(
tex: string,
plugin: ExcalidrawPlugin,
): Promise<{
mimeType: MimeType;
fileId: FileId;
dataURL: DataURL;
created: number;
size: { height: number; width: number };
}> {
const eq = plugin.mathjax.tex2svg(tex, { display: true, scale: 4 });
const svg = eq.querySelector("svg");
if (svg) {
const dataURL = svgToBase64(svg.outerHTML);
return {
mimeType: "image/svg+xml",
fileId: fileid() as FileId,
dataURL: dataURL as DataURL,
created: Date.now(),
size: await getImageSize(dataURL),
};
}
return null;
}
async function mathjaxImage2html(tex: string): Promise<{
mimeType: MimeType;
fileId: FileId;
dataURL: DataURL;
created: number;
size: { height: number; width: number };
}> {
const div = document.body.createDiv();
div.style.display = "table"; //this will ensure div fits width of formula exactly
//@ts-ignore
const eq = window.MathJax.tex2chtml(tex, { display: true, scale: 4 }); //scale to ensure good resolution
eq.style.margin = "3px";
eq.style.color = "black";
//ipad support - removing mml as that was causing phantom double-image blur.
const el = eq.querySelector("mjx-assistive-mml");
if (el) {
el.parentElement.removeChild(el);
}
div.appendChild(eq);
window.MathJax.typeset();
const canvas = await html2canvas(div, { backgroundColor: null }); //transparent
document.body.removeChild(div);
return {
mimeType: "image/png",
fileId: fileid() as FileId,
dataURL: canvas.toDataURL() as DataURL,
created: Date.now(),
size: { height: canvas.height, width: canvas.width },
};
}

View File

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

371
src/OneOffs.ts Normal file
View File

@@ -0,0 +1,371 @@
import { App, Modal, TFile } from "obsidian";
import { FRONTMATTER_KEY } from "./constants";
import { ExcalidrawData, getJSON } from "./ExcalidrawData";
import { getTextMode, TextMode } from "./ExcalidrawView";
import ExcalidrawPlugin from "./main";
import { errorlog, log } from "./Utils";
export class OneOffs {
private plugin: ExcalidrawPlugin;
constructor(plugin: ExcalidrawPlugin) {
this.plugin = plugin;
}
public patchCommentBlock() {
//This is a once off cleanup process to remediate incorrectly placed comment %% before # Text Elements
if (!this.plugin.settings.patchCommentBlock) {
return;
}
const plugin = this.plugin;
log(
`${window
.moment()
.format("HH:mm:ss")}: Excalidraw will patch drawings in 5 minutes`,
);
setTimeout(async () => {
await plugin.loadSettings();
if (!plugin.settings.patchCommentBlock) {
log(
`${window
.moment()
.format(
"HH:mm:ss",
)}: Excalidraw patching aborted because synched data.json is already patched`,
);
return;
}
log(
`${window
.moment()
.format("HH:mm:ss")}: Excalidraw is starting the patching process`,
);
let i = 0;
const excalidrawFiles = plugin.app.vault.getFiles();
for (const f of (excalidrawFiles || []).filter((f: TFile) =>
plugin.isExcalidrawFile(f),
)) {
if (
f.extension !== "excalidraw" && //legacy files do not need to be touched
plugin.app.workspace.getActiveFile() !== f
) {
//file is currently being edited
let drawing = await plugin.app.vault.read(f);
const orig_drawing = drawing;
drawing = drawing.replaceAll("\r\n", "\n").replaceAll("\r", "\n"); //Win, Mac, Linux compatibility
drawing = drawing.replace(
"\n%%\n# Text Elements\n",
"\n# Text Elements\n",
);
if (drawing.search("\n%%\n# Drawing\n") === -1) {
const sceneJSONandPOS = getJSON(drawing);
drawing = `${drawing.substr(
0,
sceneJSONandPOS.pos,
)}\n%%\n# Drawing\n\`\`\`json\n${sceneJSONandPOS.scene}\n\`\`\`%%`;
}
if (drawing !== orig_drawing) {
i++;
log(`Excalidraw patched: ${f.path}`);
await plugin.app.vault.modify(f, drawing);
}
}
}
plugin.settings.patchCommentBlock = false;
plugin.saveSettings();
log(
`${window
.moment()
.format("HH:mm:ss")}: Excalidraw patched in total ${i} files`,
);
}, 300000); //5 minutes
}
public migrationNotice() {
if (this.plugin.settings.loadCount > 0) {
return;
}
const plugin = this.plugin;
plugin.app.workspace.onLayoutReady(async () => {
plugin.settings.loadCount++;
plugin.saveSettings();
const files = plugin.app.vault
.getFiles()
.filter((f) => f.extension === "excalidraw");
if (files.length > 0) {
const prompt = new MigrationPrompt(plugin.app, plugin);
prompt.open();
}
});
}
public imageElementLaunchNotice() {
if (!this.plugin.settings.imageElementNotice) {
return;
}
const plugin = this.plugin;
plugin.app.workspace.onLayoutReady(async () => {
const prompt = new ImageElementNotice(plugin.app, plugin);
prompt.open();
});
}
public wysiwygPatch() {
if (this.plugin.settings.patchCommentBlock) {
return;
} //the comment block patch needs to happen first (unlikely that someone has waited this long with the update...)
//This is a once off process to patch excalidraw files remediate incorrectly placed comment %% before # Text Elements
if (
!(
this.plugin.settings.runWYSIWYGpatch ||
this.plugin.settings.fixInfinitePreviewLoop
)
) {
return;
}
const plugin = this.plugin;
log(
`${window
.moment()
.format(
"HH:mm:ss",
)}: Excalidraw will patch drawings to support WYSIWYG in 7 minutes`,
);
setTimeout(async () => {
await plugin.loadSettings();
if (
!(
this.plugin.settings.runWYSIWYGpatch ||
this.plugin.settings.fixInfinitePreviewLoop
)
) {
log(
`${window
.moment()
.format(
"HH:mm:ss",
)}: Excalidraw patching aborted because synched data.json is already patched`,
);
return;
}
log(
`${window
.moment()
.format("HH:mm:ss")}: Excalidraw is starting the patching process`,
);
let i = 0;
const excalidrawFiles = plugin.app.vault.getFiles();
for (const f of (excalidrawFiles || []).filter((f: TFile) =>
plugin.isExcalidrawFile(f),
)) {
if (
f.extension !== "excalidraw" && //legacy files do not need to be touched
plugin.app.workspace.getActiveFile() !== f
) {
//file is currently being edited
try {
const excalidrawData = new ExcalidrawData(plugin);
const data = await plugin.app.vault.read(f);
const textMode = getTextMode(data);
await excalidrawData.loadData(data, f, textMode);
let trimLocation = data.search(/(^%%\n)?# Text Elements\n/m);
if (trimLocation == -1) {
trimLocation = data.search(/(%%\n)?# Drawing\n/);
}
if (trimLocation > -1) {
let header = data
.substring(0, trimLocation)
.replace(
/excalidraw-plugin:\s.*\n/,
`${FRONTMATTER_KEY}: ${
textMode == TextMode.raw ? "raw\n" : "parsed\n"
}`,
);
header = header.replace(
/cssclass:[\s]*excalidraw-hide-preview-text[\s]*\n/,
"",
);
const REG_IMG = /(^---[\w\W]*?---\n)(!\[\[.*?]]\n(%%\n)?)/m; //(%%\n)? because of 1.4.8-beta... to be backward compatible with anyone who installed that version
if (header.match(REG_IMG)) {
header = header.replace(REG_IMG, "$1");
}
const newData = header + excalidrawData.generateMD();
if (data !== newData) {
i++;
log(`Excalidraw patched: ${f.path}`);
await plugin.app.vault.modify(f, newData);
}
}
} catch (e) {
errorlog({
where: "OneOffs.wysiwygPatch",
message: `Unable to process: ${f.path}`,
error: e,
});
}
}
}
plugin.settings.runWYSIWYGpatch = false;
plugin.settings.fixInfinitePreviewLoop = false;
plugin.saveSettings();
log(
`${window
.moment()
.format("HH:mm:ss")}: Excalidraw patched in total ${i} files`,
);
}, 420000); //7 minutes
}
}
class MigrationPrompt extends Modal {
private plugin: ExcalidrawPlugin;
constructor(app: App, plugin: ExcalidrawPlugin) {
super(app);
this.plugin = plugin;
}
onOpen(): void {
this.titleEl.setText("Welcome to Excalidraw 1.2");
this.createForm();
}
onClose(): void {
this.contentEl.empty();
}
createForm(): void {
const div = this.contentEl.createDiv();
// div.addClass("excalidraw-prompt-div");
// div.style.maxWidth = "600px";
div.createEl("p", {
text: "This version comes with tons of new features and possibilities. Please read the description in Community Plugins to find out more.",
});
div.createEl("p", { text: "" }, (el) => {
el.innerHTML =
"Drawings you've created with version 1.1.x need to be converted to take advantage of the new features. You can also continue to use them in compatibility mode. " +
"During conversion your old *.excalidraw files will be replaced with new *.excalidraw.md files.";
});
div.createEl("p", { text: "" }, (el) => {
//files manually follow one of two options:
el.innerHTML =
"To convert your drawings you have the following options:<br><ul>" +
"<li>Click <code>CONVERT FILES</code> now to convert all of your *.excalidraw files, or if you prefer to make a backup first, then click <code>CANCEL</code>.</li>" +
"<li>In the Command Palette select <code>Excalidraw: Convert *.excalidraw files to *.excalidraw.md files</code></li>" +
"<li>Right click an <code>*.excalidraw</code> file in File Explorer and select one of the following options to convert files one by one: <ul>" +
"<li><code>*.excalidraw => *.excalidraw.md</code></li>" +
"<li><code>*.excalidraw => *.md (Logseq compatibility)</code>. This option will retain the original *.excalidraw file next to the new Obsidian format. " +
"Make sure you also enable <code>Compatibility features</code> in Settings for a full solution.</li></ul></li>" +
"<li>Open a drawing in compatibility mode and select <code>Convert to new format</code> from the <code>Options Menu</code></li></ul>";
});
div.createEl("p", {
text: "This message will only appear maximum 3 times in case you have *.excalidraw files in your Vault.",
});
const bConvert = div.createEl("button", { text: "CONVERT FILES" });
bConvert.onclick = () => {
this.plugin.convertExcalidrawToMD();
this.close();
};
const bCancel = div.createEl("button", { text: "CANCEL" });
bCancel.onclick = () => {
this.close();
};
}
}
class ImageElementNotice extends Modal {
private plugin: ExcalidrawPlugin;
private saveChanges: boolean = false;
constructor(app: App, plugin: ExcalidrawPlugin) {
super(app);
this.plugin = plugin;
}
onOpen(): void {
this.titleEl.setText("Image Elements have arrived!");
this.createForm();
}
async onClose() {
this.contentEl.empty();
if (!this.saveChanges) {
return;
}
await this.plugin.loadSettings();
this.plugin.settings.imageElementNotice = false;
this.plugin.saveSettings();
}
createForm(): void {
const div = this.contentEl.createDiv();
//div.addClass("excalidraw-prompt-div");
//div.style.maxWidth = "600px";
div.createEl("p", { text: "" }, (el) => {
el.innerHTML =
"Welcome to Obsidian-Excalidraw 1.4! I've added Image Elements. " +
"Please watch the video below to learn how to use this new feature.";
});
div.createEl("p", { text: "" }, (el) => {
el.innerHTML =
"<u>⚠ WARNING:</u> Opening new drawings with an older version of the plugin will lead to loss of images. " +
"Update the plugin on all your devices.";
});
div.createEl("p", { text: "" }, (el) => {
el.innerHTML =
"Since March, I have spent most of my free time building this plugin. Close to 75 workdays worth of my time (assuming 8-hour days). " +
"Some of you have already bought me a coffee. THANK YOU! Your support really means a lot to me! If you have not yet done so, please consider clicking the button below.";
});
const coffeeDiv = div.createDiv("coffee");
coffeeDiv.addClass("ex-coffee-div");
const coffeeLink = coffeeDiv.createEl("a", {
href: "https://ko-fi.com/zsolt",
});
const coffeeImg = coffeeLink.createEl("img", {
attr: {
src: "https://cdn.ko-fi.com/cdn/kofi3.png?v=3",
},
});
coffeeImg.height = 45;
div.createEl("p", { text: "" }, (el) => {
//files manually follow one of two options:
el.style.textAlign = "center";
el.innerHTML =
'<iframe width="560" height="315" src="https://www.youtube.com/embed/_c_0zpBJ4Xc?start=20" title="YouTube video player" ' +
'frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" ' +
"allowfullscreen></iframe>";
});
div.createEl("p", { text: "" }, (el) => {
//files manually follow one of two options:
el.style.textAlign = "right";
const bOk = el.createEl("button", { text: "OK - Don't show this again" });
bOk.onclick = () => {
this.saveChanges = true;
this.close();
};
const bCancel = el.createEl("button", {
text: "CANCEL - Read next time",
});
bCancel.onclick = () => {
this.saveChanges = false;
this.close();
};
});
}
}

265
src/Prompt.ts Normal file
View File

@@ -0,0 +1,265 @@
import {
App,
ButtonComponent,
Modal,
TextComponent,
FuzzyMatch,
FuzzySuggestModal,
} from "obsidian";
export class Prompt extends Modal {
private promptEl: HTMLInputElement;
private resolve: (value: string) => void;
constructor(
app: App,
private prompt_text: string,
private default_value?: string,
private placeholder?: string,
private prompt_desc?: string,
) {
super(app);
}
onOpen(): void {
this.titleEl.setText(this.prompt_text);
this.createForm();
}
onClose(): void {
this.contentEl.empty();
}
createForm(): void {
let div = this.contentEl.createDiv();
div.addClass("excalidraw-prompt-div");
if (this.prompt_desc) {
div = div.createDiv();
div.style.width = "100%";
const p = div.createEl("p");
p.innerHTML = this.prompt_desc;
}
const form = div.createEl("form");
form.addClass("excalidraw-prompt-form");
form.type = "submit";
form.onsubmit = (e: Event) => {
e.preventDefault();
this.resolve(this.promptEl.value);
this.close();
};
this.promptEl = form.createEl("input");
this.promptEl.type = "text";
this.promptEl.placeholder = this.placeholder;
this.promptEl.value = this.default_value ?? "";
this.promptEl.addClass("excalidraw-prompt-input");
this.promptEl.select();
}
async openAndGetValue(resolve: (value: string) => void): Promise<void> {
this.resolve = resolve;
this.open();
}
}
export class GenericInputPrompt extends Modal {
public waitForClose: Promise<string>;
private resolvePromise: (input: string) => void;
private rejectPromise: (reason?: any) => void;
private didSubmit: boolean = false;
private inputComponent: TextComponent;
private input: string;
private readonly placeholder: string;
public static Prompt(
app: App,
header: string,
placeholder?: string,
value?: string,
): Promise<string> {
const newPromptModal = new GenericInputPrompt(
app,
header,
placeholder,
value,
);
return newPromptModal.waitForClose;
}
protected constructor(
app: App,
private header: string,
placeholder?: string,
value?: string,
) {
super(app);
this.placeholder = placeholder;
this.input = value;
this.waitForClose = new Promise<string>((resolve, reject) => {
this.resolvePromise = resolve;
this.rejectPromise = reject;
});
this.display();
this.open();
}
private display() {
this.contentEl.empty();
this.titleEl.textContent = this.header;
const mainContentContainer: HTMLDivElement = this.contentEl.createDiv();
this.inputComponent = this.createInputField(
mainContentContainer,
this.placeholder,
this.input,
);
this.createButtonBar(mainContentContainer);
}
protected createInputField(
container: HTMLElement,
placeholder?: string,
value?: string,
) {
const textComponent = new TextComponent(container);
textComponent.inputEl.style.width = "100%";
textComponent
.setPlaceholder(placeholder ?? "")
.setValue(value ?? "")
.onChange((value) => (this.input = value))
.inputEl.addEventListener("keydown", this.submitEnterCallback);
return textComponent;
}
private createButton(
container: HTMLElement,
text: string,
callback: (evt: MouseEvent) => any,
) {
const btn = new ButtonComponent(container);
btn.setButtonText(text).onClick(callback);
return btn;
}
private createButtonBar(mainContentContainer: HTMLDivElement) {
const buttonBarContainer: HTMLDivElement = mainContentContainer.createDiv();
this.createButton(
buttonBarContainer,
"Ok",
this.submitClickCallback,
).setCta().buttonEl.style.marginRight = "0";
this.createButton(buttonBarContainer, "Cancel", this.cancelClickCallback);
buttonBarContainer.style.display = "flex";
buttonBarContainer.style.flexDirection = "row-reverse";
buttonBarContainer.style.justifyContent = "flex-start";
buttonBarContainer.style.marginTop = "1rem";
}
private submitClickCallback = () => this.submit();
private cancelClickCallback = () => this.cancel();
private submitEnterCallback = (evt: KeyboardEvent) => {
if (evt.key === "Enter") {
evt.preventDefault();
this.submit();
}
};
private submit() {
this.didSubmit = true;
this.close();
}
private cancel() {
this.close();
}
private resolveInput() {
if (!this.didSubmit) {
this.rejectPromise("No input given.");
} else {
this.resolvePromise(this.input);
}
}
private removeInputListener() {
this.inputComponent.inputEl.removeEventListener(
"keydown",
this.submitEnterCallback,
);
}
onOpen() {
super.onOpen();
this.inputComponent.inputEl.focus();
this.inputComponent.inputEl.select();
}
onClose() {
super.onClose();
this.resolveInput();
this.removeInputListener();
}
}
export class GenericSuggester extends FuzzySuggestModal<string> {
private resolvePromise: (value: string) => void;
private rejectPromise: (reason?: any) => void;
public promise: Promise<string>;
private resolved: boolean;
public static Suggest(app: App, displayItems: string[], items: string[]) {
const newSuggester = new GenericSuggester(app, displayItems, items);
return newSuggester.promise;
}
public constructor(
app: App,
private displayItems: string[],
private items: string[],
) {
super(app);
this.promise = new Promise<string>((resolve, reject) => {
this.resolvePromise = resolve;
this.rejectPromise = reject;
});
this.open();
}
getItemText(item: string): string {
return this.displayItems[this.items.indexOf(item)];
}
getItems(): string[] {
return this.items;
}
selectSuggestion(value: FuzzyMatch<string>, evt: MouseEvent | KeyboardEvent) {
this.resolved = true;
super.selectSuggestion(value, evt);
}
onChooseItem(item: string): void {
this.resolved = true;
this.resolvePromise(item);
}
onClose() {
super.onClose();
if (!this.resolved) {
this.rejectPromise("no input given.");
}
}
}

View File

@@ -0,0 +1,43 @@
import { App, MarkdownRenderer, Modal, Notice, request } from "obsidian";
import { Url } from "url";
import { t } from "./lang/helpers";
import ExcalidrawPlugin from "./main";
import { errorlog } from "./Utils";
const URL =
"https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/index.md";
export class ScriptInstallPrompt extends Modal {
constructor(private plugin: ExcalidrawPlugin) {
super(plugin.app);
// this.titleEl.setText(t("INSTAL_MODAL_TITLE"));
}
async onOpen(): Promise<void> {
this.contentEl.classList.add("excalidraw-scriptengine-install");
this.containerEl.classList.add("excalidraw-scriptengine-install");
try {
const source = await request({ url: URL });
await MarkdownRenderer.renderMarkdown(
source,
this.contentEl,
"",
this.plugin,
);
this.contentEl.querySelectorAll("h1[data-heading],h2[data-heading],h3[data-heading]").forEach((el) => {
el.setAttribute("id", el.getAttribute("data-heading"));
});
this.contentEl.querySelectorAll("a.internal-link").forEach((el) => {
el.removeAttribute("target");
});
} catch (e) {
errorlog({ where: "ScriptInstallPrompt.onOpen", error: e });
new Notice("Could not open ScriptEngine repository");
this.close();
}
}
onClose(): void {
this.contentEl.empty();
}
}

195
src/Scripts.ts Normal file
View File

@@ -0,0 +1,195 @@
import { sub } from "@zsviczian/excalidraw/types/ga";
import { App, TAbstractFile, TFile } from "obsidian";
import { VIEW_TYPE_EXCALIDRAW } from "./constants";
import ExcalidrawView from "./ExcalidrawView";
import ExcalidrawPlugin from "./main";
import { GenericInputPrompt, GenericSuggester } from "./Prompt";
import { splitFolderAndFilename } from "./Utils";
export class ScriptEngine {
private plugin: ExcalidrawPlugin;
private scriptPath: string;
constructor(plugin: ExcalidrawPlugin) {
this.plugin = plugin;
this.loadScripts();
this.registerEventHandlers();
}
registerEventHandlers() {
const deleteEventHandler = async (file: TFile) => {
if (!(file instanceof TFile)) {
return;
}
if (!file.path.startsWith(this.scriptPath)) {
return;
}
this.unloadScript(this.getScriptName(file));
};
this.plugin.registerEvent(
this.plugin.app.vault.on("delete", deleteEventHandler),
);
const createEventHandler = async (file: TFile) => {
if (!(file instanceof TFile)) {
return;
}
if (!file.path.startsWith(this.scriptPath)) {
return;
}
this.loadScript(file);
};
this.plugin.registerEvent(
this.plugin.app.vault.on("create", createEventHandler),
);
const renameEventHandler = async (file: TAbstractFile, oldPath: string) => {
if (!(file instanceof TFile)) {
return;
}
const oldFileIsScript = oldPath.startsWith(this.scriptPath);
const newFileIsScript = file.path.startsWith(this.scriptPath);
if (oldFileIsScript) {
this.unloadScript(this.getScriptName(oldPath));
}
if (newFileIsScript) {
this.loadScript(file);
}
};
this.plugin.registerEvent(
this.plugin.app.vault.on("rename", renameEventHandler),
);
}
updateScriptPath() {
if (this.scriptPath === this.plugin.settings.scriptFolderPath) {
return;
}
this.unloadScripts();
this.loadScripts();
}
loadScripts() {
const app = this.plugin.app;
this.scriptPath = this.plugin.settings.scriptFolderPath;
const scripts = app.vault
.getFiles()
.filter((f: TFile) => f.path.startsWith(this.scriptPath));
scripts.forEach((f) => this.loadScript(f));
}
getScriptName(f:TFile|string):string {
let basename = "";
let path = "";
if(f instanceof TFile) {
basename = f.basename;
path = f.path;
} else {
basename = splitFolderAndFilename(f).basename;
path = f;
}
const subpath = path.split(
`${this.plugin.settings.scriptFolderPath}/`,
)[1];
const lastSlash = subpath.lastIndexOf("/");
if (lastSlash > -1) {
return subpath.substring(0, lastSlash + 1) + basename;
}
return basename;
}
loadScript(f: TFile) {
const scriptName = this.getScriptName(f);
this.plugin.addCommand({
id: scriptName,
name: `(Script) ${scriptName}`,
checkCallback: (checking: boolean) => {
if (checking) {
return (
this.plugin.app.workspace.activeLeaf.view.getViewType() ==
VIEW_TYPE_EXCALIDRAW
);
}
const view = this.plugin.app.workspace.activeLeaf.view;
if (view instanceof ExcalidrawView) {
this.executeScript(view, f);
return true;
}
return false;
},
});
}
unloadScripts() {
const app = this.plugin.app;
const scripts = app.vault
.getFiles()
.filter((f: TFile) => f.path.startsWith(this.scriptPath));
scripts.forEach((f) => {
this.unloadScript(this.getScriptName(f));
});
}
unloadScript(basename: string) {
const app = this.plugin.app;
const commandId = `obsidian-excalidraw-plugin:${basename}`;
// @ts-ignore
if (!app.commands.commands[commandId]) {
return;
}
// @ts-ignore
delete app.commands.commands[commandId];
}
async executeScript(view: ExcalidrawView, f: TFile) {
if (!view || !f) {
return;
}
this.plugin.ea.reset();
this.plugin.ea.setView(view);
const script = await this.plugin.app.vault.read(f);
if (!script) {
return;
}
this.plugin.ea.activeScript = this.getScriptName(f);
//https://stackoverflow.com/questions/45381204/get-asyncfunction-constructor-in-typescript changed tsconfig to es2017
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
const result = await new AsyncFunction("ea", "utils", script)(this.plugin.ea, {
inputPrompt: (header: string, placeholder?: string, value?: string) =>
ScriptEngine.inputPrompt(this.plugin.app, header, placeholder, value),
suggester: (displayItems: string[], items: string[]) =>
ScriptEngine.suggester(this.plugin.app, displayItems, items),
});
this.plugin.ea.activeScript = null;
return result;
}
public static async inputPrompt(
app: App,
header: string,
placeholder?: string,
value?: string,
) {
try {
return await GenericInputPrompt.Prompt(app, header, placeholder, value);
} catch {
return undefined;
}
}
public static async suggester(
app: App,
displayItems: string[],
items: string[],
) {
try {
return await GenericSuggester.Suggest(app, displayItems, items);
} catch {
return undefined;
}
}
}

477
src/Utils.ts Normal file
View File

@@ -0,0 +1,477 @@
import { exportToSvg, exportToBlob } from "@zsviczian/excalidraw";
import {
App,
normalizePath,
Notice,
TAbstractFile,
TFolder,
Vault,
WorkspaceLeaf,
} from "obsidian";
import { Random } from "roughjs/bin/math";
import { Zoom } from "@zsviczian/excalidraw/types/types";
import { CASCADIA_FONT, REG_BLOCK_REF_CLEAN, VIRGIL_FONT } from "./constants";
import ExcalidrawPlugin from "./main";
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/element/types";
import { ExportSettings } from "./ExcalidrawView";
declare module "obsidian" {
interface Workspace {
getAdjacentLeafInDirection(
leaf: WorkspaceLeaf,
direction: string,
): WorkspaceLeaf;
}
interface Vault {
getConfig(option: "attachmentFolderPath"): string;
}
}
let versionUpdateChecked = false;
export const checkExcalidrawVersion = async (app:App) => {
if(versionUpdateChecked) return;
versionUpdateChecked = true;
//@ts-ignore
const manifest = app.plugins.manifests["obsidian-excalidraw-plugin"];
//@ts-ignore
const latestVersion = await app.plugins.getLatestVersion("obsidian-excalidraw-plugin",manifest)
if(latestVersion>manifest.version) {
new Notice(`A newer version of Excalidraw is available in Community Plugins. You are using ${manifest.version}. The latest version is ${latestVersion}`);
}
setTimeout(()=>versionUpdateChecked=false,28800000);//reset after 8 hours
}
/**
* Splits a full path including a folderpath and a filename into separate folderpath and filename components
* @param filepath
*/
export function splitFolderAndFilename(filepath: string): {
folderpath: string;
filename: string;
basename: string;
} {
const lastIndex = filepath.lastIndexOf("/");
const filename = lastIndex == -1 ? filepath : filepath.substr(lastIndex + 1);
return {
folderpath: normalizePath(filepath.substr(0, lastIndex)),
filename,
basename: filename.replace(/\.[^/.]+$/, ""),
};
}
/**
* Download data as file from Obsidian, to store on local device
* @param encoding
* @param data
* @param filename
*/
export function download(encoding: string, data: any, filename: string) {
const element = document.createElement("a");
element.setAttribute("href", (encoding ? `${encoding},` : "") + data);
element.setAttribute("download", filename);
element.style.display = "none";
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
/**
* Generates the image filename based on the excalidraw filename
* @param excalidrawPath - Full filepath of ExclidrawFile
* @param newExtension - extension of IMG file in ".extension" format
* @returns
*/
export function getIMGPathFromExcalidrawFile(
excalidrawPath: string,
newExtension: string,
): string {
const isLegacyFile: boolean = excalidrawPath.endsWith(".excalidraw");
const replaceExtension: string = isLegacyFile ? ".excalidraw" : ".md";
return (
excalidrawPath.substring(0, excalidrawPath.lastIndexOf(replaceExtension)) +
newExtension
);
}
/**
* Create new file, if file already exists find first unique filename by adding a number to the end of the filename
* @param filename
* @param folderpath
* @returns
*/
export function getNewUniqueFilepath(
vault: Vault,
filename: string,
folderpath: string,
): string {
let fname = normalizePath(`${folderpath}/${filename}`);
let file: TAbstractFile = vault.getAbstractFileByPath(fname);
let i = 0;
while (file) {
fname = normalizePath(
`${folderpath}/${filename.slice(
0,
filename.lastIndexOf("."),
)}_${i}${filename.slice(filename.lastIndexOf("."))}`,
);
i++;
file = vault.getAbstractFileByPath(fname);
}
return fname;
}
/**
* Open or create a folderpath if it does not exist
* @param folderpath
*/
export async function checkAndCreateFolder(vault: Vault, folderpath: string) {
folderpath = normalizePath(folderpath);
const folder = vault.getAbstractFileByPath(folderpath);
if (folder && folder instanceof TFolder) {
return;
}
await vault.createFolder(folderpath);
}
const random = new Random(Date.now());
export const randomInteger = () => Math.floor(random.next() * 2 ** 31);
//https://macromates.com/blog/2006/wrapping-text-with-regular-expressions/
export function wrapText(
text: string,
lineLen: number,
forceWrap: boolean = false,
tolerance: number = 0,
): string {
if (!lineLen) {
return text;
}
let outstring = "";
if (forceWrap) {
for (const t of text.split("\n")) {
const v = t.match(new RegExp(`(.){1,${lineLen}}`, "g"));
outstring += v ? `${v.join("\n")}\n` : "\n";
}
return outstring.replace(/\n$/, "");
}
// 1 2 3 4
const reg = new RegExp(
`(.{1,${lineLen}})(\\s+|$\\n?)|([^\\s]{1,${
lineLen + tolerance
}})(\\s+|$\\n?)?`,
//`(.{1,${lineLen}})(\\s+|$\\n?)|([^\\s]+)(\\s+|$\\n?)`,
"gm",
);
const res = text.matchAll(reg);
let parts;
while (!(parts = res.next()).done) {
outstring += parts.value[1]
? parts.value[1].trimEnd()
: parts.value[3].trimEnd();
const newLine =
(parts.value[2] ? parts.value[2].split("\n").length - 1 : 0) +
(parts.value[4] ? parts.value[4].split("\n").length - 1 : 0);
outstring += "\n".repeat(newLine);
if (newLine === 0) {
outstring += "\n";
}
}
return outstring.replace(/\n$/, "");
}
const rotate = (
pointX: number,
pointY: number,
centerX: number,
centerY: number,
angle: number,
): [number, number] =>
// 𝑎𝑥=(𝑎𝑥𝑐𝑥)cos𝜃(𝑎𝑦𝑐𝑦)sin𝜃+𝑐𝑥
// 𝑎𝑦=(𝑎𝑥𝑐𝑥)sin𝜃+(𝑎𝑦𝑐𝑦)cos𝜃+𝑐𝑦.
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
[
(pointX - centerX) * Math.cos(angle) -
(pointY - centerY) * Math.sin(angle) +
centerX,
(pointX - centerX) * Math.sin(angle) +
(pointY - centerY) * Math.cos(angle) +
centerY,
];
export const rotatedDimensions = (
element: ExcalidrawElement,
): [number, number, number, number] => {
if (element.angle === 0) {
return [element.x, element.y, element.width, element.height];
}
const centerX = element.x + element.width / 2;
const centerY = element.y + element.height / 2;
const [left, top] = rotate(
element.x,
element.y,
centerX,
centerY,
element.angle,
);
const [right, bottom] = rotate(
element.x + element.width,
element.y + element.height,
centerX,
centerY,
element.angle,
);
return [
left < right ? left : right,
top < bottom ? top : bottom,
Math.abs(left - right),
Math.abs(top - bottom),
];
};
export const viewportCoordsToSceneCoords = (
{ clientX, clientY }: { clientX: number; clientY: number },
{
zoom,
offsetLeft,
offsetTop,
scrollX,
scrollY,
}: {
zoom: Zoom;
offsetLeft: number;
offsetTop: number;
scrollX: number;
scrollY: number;
},
) => {
const invScale = 1 / zoom.value;
const x = (clientX - zoom.translation.x - offsetLeft) * invScale - scrollX;
const y = (clientY - zoom.translation.y - offsetTop) * invScale - scrollY;
return { x, y };
};
export const getNewOrAdjacentLeaf = (
plugin: ExcalidrawPlugin,
leaf: WorkspaceLeaf,
): WorkspaceLeaf => {
if (plugin.settings.openInAdjacentPane) {
let leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(
leaf,
"right",
);
if (!leafToUse) {
leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(leaf, "left");
}
if (!leafToUse) {
leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(
leaf,
"bottom",
);
}
if (!leafToUse) {
leafToUse = plugin.app.workspace.getAdjacentLeafInDirection(leaf, "top");
}
if (!leafToUse) {
leafToUse = plugin.app.workspace.createLeafBySplit(leaf);
}
return leafToUse;
}
return plugin.app.workspace.createLeafBySplit(leaf);
};
export const svgToBase64 = (svg: string): string => {
return `data:image/svg+xml;base64,${btoa(
unescape(encodeURIComponent(svg.replaceAll("&nbsp;", " "))),
)}`;
};
export const getBinaryFileFromDataURL = (dataURL: string): ArrayBuffer => {
if (!dataURL) {
return null;
}
const parts = dataURL.matchAll(/base64,(.*)/g).next();
const binary_string = window.atob(parts.value[1]);
const len = binary_string.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
};
export const getAttachmentsFolderAndFilePath = async (
app: App,
activeViewFilePath: string,
newFileName: string,
): Promise<{ folder: string; filepath: string }> => {
let folder = app.vault.getConfig("attachmentFolderPath");
// folder == null: save to vault root
// folder == "./" save to same folder as current file
// folder == "folder" save to specific folder in vault
// folder == "./folder" save to specific subfolder of current active folder
if (folder && folder.startsWith("./")) {
// folder relative to current file
const activeFileFolder = `${
splitFolderAndFilename(activeViewFilePath).folderpath
}/`;
folder = normalizePath(activeFileFolder + folder.substring(2));
}
if (!folder) {
folder = "";
}
await checkAndCreateFolder(app.vault, folder);
return {
folder,
filepath: normalizePath(`${folder}/${newFileName}`),
};
};
export const getSVG = async (
scene: any,
exportSettings: ExportSettings,
): Promise<SVGSVGElement> => {
try {
return await exportToSvg({
elements: scene.elements,
appState: {
exportBackground: exportSettings.withBackground,
exportWithDarkMode: exportSettings.withTheme
? scene.appState?.theme != "light"
: false,
...scene.appState,
},
files: scene.files,
exportPadding: 10,
});
} catch (error) {
return null;
}
};
export const getPNG = async (
scene: any,
exportSettings: ExportSettings,
scale: number = 1,
) => {
try {
return await exportToBlob({
elements: scene.elements,
appState: {
exportBackground: exportSettings.withBackground,
exportWithDarkMode: exportSettings.withTheme
? scene.appState?.theme != "light"
: false,
...scene.appState,
},
files: scene.files,
mimeType: "image/png",
getDimensions: (width: number, height: number) => ({
width: width * scale,
height: height * scale,
scale,
}),
});
} catch (error) {
errorlog({ where: "Utils.getPNG", error });
return null;
}
};
export const embedFontsInSVG = (svg: SVGSVGElement): SVGSVGElement => {
//replace font references with base64 fonts
const includesVirgil =
svg.querySelector("text[font-family^='Virgil']") != null;
const includesCascadia =
svg.querySelector("text[font-family^='Cascadia']") != null;
const defs = svg.querySelector("defs");
if (defs && (includesCascadia || includesVirgil)) {
defs.innerHTML = `<style>${includesVirgil ? VIRGIL_FONT : ""}${
includesCascadia ? CASCADIA_FONT : ""
}</style>`;
}
return svg;
};
export const getImageSize = async (
src: string,
): Promise<{ height: number; width: number }> => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve({ height: img.height, width: img.width });
img.onerror = reject;
img.src = src;
});
};
export const scaleLoadedImage = (
scene: any,
files: any,
): { dirty: boolean; scene: any } => {
let dirty = false;
if (!files || !scene) {
return { dirty, scene };
}
for (const f of files) {
const [w_image, h_image] = [f.size.width, f.size.height];
const imageAspectRatio = f.size.width / f.size.height;
scene.elements
.filter((e: any) => e.type === "image" && e.fileId === f.id)
.forEach((el: any) => {
const [w_old, h_old] = [el.width, el.height];
const elementAspectRatio = w_old / h_old;
if (imageAspectRatio != elementAspectRatio) {
dirty = true;
const h_new = Math.sqrt((w_old * h_old * h_image) / w_image);
const w_new = Math.sqrt((w_old * h_old * w_image) / h_image);
el.height = h_new;
el.width = w_new;
el.y += (h_old - h_new) / 2;
el.x += (w_old - w_new) / 2;
}
});
return { dirty, scene };
}
};
export const isObsidianThemeDark = () =>
document.body.classList.contains("theme-dark");
export function getIMGFilename(path: string, extension: string): string {
return `${path.substring(0, path.lastIndexOf("."))}.${extension}`;
}
export type LinkParts = {
original: string;
path: string;
isBlockRef: boolean;
ref: string;
width: number;
height: number;
};
export const getLinkParts = (fname: string): LinkParts => {
const REG = /(^[^#\|]+)#?(\^)?([^\|]*)?\|?(\d*)x?(\d*)/;
const parts = fname.match(REG);
return {
original: fname,
path: parts[1],
isBlockRef: parts[2] === "^",
ref: parts[3]?.replaceAll(REG_BLOCK_REF_CLEAN, ""),
width: parts[4] ? parseInt(parts[4]) : undefined,
height: parts[5] ? parseInt(parts[5]) : undefined,
};
};
export const errorlog = (data: {}) => {
console.error({ plugin: "Excalidraw", ...data });
};
export const sleep = async (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
export const log = console.log.bind(window.console);
export const debug = console.log.bind(window.console);
//export const debug = function(){};

File diff suppressed because one or more lines are too long

67
src/lang/helpers.ts Normal file
View File

@@ -0,0 +1,67 @@
//Solution copied from obsidian-kanban: https://github.com/mgmeyers/obsidian-kanban/blob/44118e25661bff9ebfe54f71ae33805dc88ffa53/src/lang/helpers.ts
import { moment } from "obsidian";
import { errorlog } from "src/Utils";
import ar from "./locale/ar";
import cz from "./locale/cz";
import da from "./locale/da";
import de from "./locale/de";
import en from "./locale/en";
import enGB from "./locale/en-gb";
import es from "./locale/es";
import fr from "./locale/fr";
import hi from "./locale/hi";
import id from "./locale/id";
import it from "./locale/it";
import ja from "./locale/ja";
import ko from "./locale/ko";
import nl from "./locale/nl";
import no from "./locale/no";
import pl from "./locale/pl";
import pt from "./locale/pt";
import ptBR from "./locale/pt-br";
import ro from "./locale/ro";
import ru from "./locale/ru";
import tr from "./locale/tr";
import zhCN from "./locale/zh-cn";
import zhTW from "./locale/zh-tw";
const localeMap: { [k: string]: Partial<typeof en> } = {
ar,
cs: cz,
da,
de,
en,
"en-gb": enGB,
es,
fr,
hi,
id,
it,
ja,
ko,
nl,
nn: no,
pl,
pt,
"pt-br": ptBR,
ro,
ru,
tr,
"zh-cn": zhCN,
"zh-tw": zhTW,
};
const locale = localeMap[moment.locale()];
export function t(str: keyof typeof en): string {
if (!locale) {
errorlog({
where: "helpers.t",
message: "Error: Excalidraw locale not found",
locale: moment.locale(),
});
}
return (locale && locale[str]) || en[str];
}

3
src/lang/locale/ar.ts Normal file
View File

@@ -0,0 +1,3 @@
// العربية
export default {};

3
src/lang/locale/cz.ts Normal file
View File

@@ -0,0 +1,3 @@
// čeština
export default {};

3
src/lang/locale/da.ts Normal file
View File

@@ -0,0 +1,3 @@
// Dansk
export default {};

3
src/lang/locale/de.ts Normal file
View File

@@ -0,0 +1,3 @@
// Deutsch
export default {};

3
src/lang/locale/en-gb.ts Normal file
View File

@@ -0,0 +1,3 @@
// British English
export default {};

293
src/lang/locale/en.ts Normal file
View File

@@ -0,0 +1,293 @@
import {
FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS,
FRONTMATTER_KEY_CUSTOM_PREFIX,
FRONTMATTER_KEY_CUSTOM_URL_PREFIX,
} from "src/constants";
// English
export default {
// main.ts
INSTALL_SCRIPT: "Install this script",
UPDATE_SCRIPT: "An update is available - Click to install",
CHECKING_SCRIPT: "Checking if a newer version is available - Click to reinstall now",
UNABLETOCHECK_SCRIPT: "Update check was unsuccessful - Click to reinstall now",
UPTODATE_SCRIPT: "Script is installed and up to date - Click to reinstall now",
OPEN_AS_EXCALIDRAW: "Open as Excalidraw Drawing",
TOGGLE_MODE: "Toggle between Excalidraw and Markdown mode",
CONVERT_NOTE_TO_EXCALIDRAW: "Convert empty note to Excalidraw Drawing",
CONVERT_EXCALIDRAW: "Convert *.excalidraw to *.md files",
CREATE_NEW: "New Excalidraw drawing",
CONVERT_FILE_KEEP_EXT: "*.excalidraw => *.excalidraw.md",
CONVERT_FILE_REPLACE_EXT: "*.excalidraw => *.md (Logseq compatibility)",
DOWNLOAD_LIBRARY: "Export stencil library as an *.excalidrawlib file",
OPEN_EXISTING_NEW_PANE: "Open an existing drawing - IN A NEW PANE",
OPEN_EXISTING_ACTIVE_PANE:
"Open an existing drawing - IN THE CURRENT ACTIVE PANE",
TRANSCLUDE: "Transclude (embed) a drawing",
TRANSCLUDE_MOST_RECENT: "Transclude (embed) the most recently edited drawing",
NEW_IN_NEW_PANE: "Create a new drawing - IN A NEW PANE",
NEW_IN_ACTIVE_PANE: "Create a new drawing - IN THE CURRENT ACTIVE PANE",
NEW_IN_NEW_PANE_EMBED:
"Create a new drawing - IN A NEW PANE - and embed into active document",
NEW_IN_ACTIVE_PANE_EMBED:
"Create a new drawing - IN THE CURRENT ACTIVE PANE - and embed into active document",
EXPORT_SVG: "Save as SVG next to the current file",
EXPORT_PNG: "Save as PNG next to the current file",
TOGGLE_LOCK: "Toggle Text Element edit RAW/PREVIEW",
DELETE_FILE: "Delete selected Image or Markdown file from Obsidian Vault",
INSERT_LINK: "Insert link to file",
INSERT_IMAGE: "Insert image from vault",
INSERT_MD: "Insert markdown file from vault",
INSERT_LATEX:
"Insert LaTeX formula (e.g. \\binom{n}{k} = \\frac{n!}{k!(n-k)!})",
ENTER_LATEX: "Enter a valid LaTeX expression",
//ExcalidrawView.ts
INSTALL_SCRIPT_BUTTON: "Install or update Excalidraw Scripts",
OPEN_AS_MD: "Open as Markdown",
SAVE_AS_PNG: "Save as PNG into Vault (CTRL/CMD+CLICK to export)",
SAVE_AS_SVG: "Save as SVG into Vault (CTRL/CMD+CLICK to export)",
OPEN_LINK: "Open selected text as link\n(SHIFT+CLICK to open in a new pane)",
EXPORT_EXCALIDRAW: "Export to an .Excalidraw file",
LINK_BUTTON_CLICK_NO_TEXT:
"Select a an ImageElement, or select a TextElement that contains an internal or external link.\n" +
"SHIFT CLICK this button to open the link in a new pane.\n" +
"CTRL/CMD CLICK the Image or TextElement on the canvas has the same effect!",
TEXT_ELEMENT_EMPTY:
"No ImageElement is selected or TextElement is empty, or [[valid-link|alias]] or [alias](valid-link) is not found",
FILENAME_INVALID_CHARS:
'File name cannot contain any of the following characters: * " \\ < > : | ?',
FILE_DOES_NOT_EXIST:
"File does not exist. Hold down ALT (or ALT+SHIFT) and CLICK link button to create a new file.",
FORCE_SAVE:
"Force-save to update transclusions in adjacent panes.\n(Please note, that autosave is always on)",
RAW: "Change to PREVIEW mode (only effects text-elements with links or transclusions)",
PARSED:
"Change to RAW mode (only effects text-elements with links or transclusions)",
NOFILE: "Excalidraw (no file)",
COMPATIBILITY_MODE:
"*.excalidraw file opened in compatibility mode. Convert to new format for full plugin functionality.",
CONVERT_FILE: "Convert to new format",
//settings.ts
FOLDER_NAME: "Excalidraw folder",
FOLDER_DESC:
"Default location for new drawings. If empty, drawings will be created in the Vault root.",
TEMPLATE_NAME: "Excalidraw template file",
TEMPLATE_DESC:
"Full filepath to the Excalidraw template. " +
"E.g.: If your template is in the default Excalidraw folder and it's name is " +
"Template.md, the setting would be: Excalidraw/Template.md (or just Excalidraw/Template - you may ommit the .md file extension" +
"If you are using Excalidraw in compatibility mode, then your template must be a legacy excalidraw file as well " +
"such as Excalidraw/Template.excalidraw.",
SCRIPT_FOLDER_NAME: "Excalidraw Automate script folder",
SCRIPT_FOLDER_DESC:
"The files you place in this folder will be treated as Excalidraw Automate scripts. " +
"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. ",
AUTOSAVE_NAME: "Autosave",
AUTOSAVE_DESC:
"Automatically save the active drawing every 30 seconds. Save normally happens when you close Excalidraw or Obsidian, or move " +
"focus to another pane. In rare cases autosave may slightly disrupt your drawing flow. I created this feature with mobile " +
"phones in mind (I only have experience with Android), where 'swiping out Obsidian to close it' led to some data loss, and because " +
"I wasn't able to force save on application termination on mobiles. If you use Excalidraw on a desktop this is likely not needed.",
FILENAME_HEAD: "Filename",
FILENAME_DESC:
"<p>The auto-generated filename consists of a prefix and a date. " +
"e.g.'Drawing 2021-05-24 12.58.07'.</p>" +
"<p>Click this link for the <a href='https://momentjs.com/docs/#/displaying/format/'>" +
"date and time format reference</a>.</p>",
FILENAME_SAMPLE: "The current file format is: <b>",
FILENAME_PREFIX_NAME: "Filename prefix",
FILENAME_PREFIX_DESC: "The first part of the filename",
FILENAME_DATE_NAME: "Filename date",
FILENAME_DATE_DESC: "The second part of the filename",
/*SVG_IN_MD_NAME: "SVG Snapshot to markdown file",
SVG_IN_MD_DESC: "If the switch is 'on' Excalidraw will include an SVG snapshot in the markdown file. "+
"When SVG snapshots are saved to the Excalidraw.md file, drawings that include large png, jpg, gif images may take extreme long time to open in markdown view. " +
"On the other hand, SVG snapshots provide some level of platform independence and longevity to your drawings. Even if Excalidraw will no longer exist, the snapshot " +
"can be opened with an app that reads SVGs. In addition hover previews will be less resource intensive if SVG snapshots are enabled.",*/
DISPLAY_HEAD: "Display",
MATCH_THEME_NAME: "New drawing to match Obsidian theme",
MATCH_THEME_DESC:
"If theme is dark, new drawing will be created in dark mode. This does not apply when you use a template for new drawings. " +
"Also this will not effect when you open an existing drawing. Those will follow the theme of the template/drawing respectively.",
MATCH_THEME_ALWAYS_NAME: "Existing drawings to match Obsidian theme",
MATCH_THEME_ALWAYS_DESC:
"If theme is dark, drawings will be opened in dark mode. If your theme is light, they will be opened in light mode. ",
MATCH_THEME_TRIGGER_NAME: "Excalidraw to follow when Obsidian Theme changes",
MATCH_THEME_TRIGGER_DESC:
"If this option is enabled open Excalidraw pane will switch to light/dark mode when Obsidian theme changes. ",
DEFAULT_OPEN_MODE_NAME: "Default mode when opening Excalidraw",
DEFAULT_OPEN_MODE_DESC:
"Specifies the mode how Excalidraw opens: Normal, Zen, or View mode. You may also set this behaviour on a file level by " +
"adding the excalidraw-default-mode frontmatter key with a value of: normal,view, or zen to your document.",
ZOOM_TO_FIT_NAME: "Zoom to fit on view resize",
ZOOM_TO_FIT_DESC: "Zoom to fit drawing when the pane is resized",
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%).",
LINKS_HEAD: "Links and transclusion",
LINKS_DESC:
"CTRL/CMD + CLICK on [[Text Elements]] to open them as links. " +
"If the selected text has more than one [[valid Obsidian links]], only the first will be opened. " +
"If the text starts as a valid web link (i.e. https:// or http://), then " +
"the plugin will open it in a browser. " +
"When Obsidian files change, the matching [[link]] in your drawings will also change. " +
"If you don't want text accidentally changing in your drawings use [[links|with aliases]].",
ADJACENT_PANE_NAME: "Open in adjacent pane",
ADJACENT_PANE_DESC:
"When CTRL/CMD+SHIFT clicking a link in Excalidraw by default the plugin will open the link in a new pane. " +
"Turning this setting on, Excalidraw will first look for an existing adjacent pane, and try to open the link there. " +
"Excalidraw will first look too the right, then to the left, then down, then up. If no pane is found, Excalidraw will open " +
"a new pane.",
LINK_BRACKETS_NAME: "Show [[brackets]] around links",
LINK_BRACKETS_DESC: `${
"In PREVIEW mode, when parsing Text Elements, place brackets around links. " +
"You can override this setting for a specific drawing by adding '"
}${FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS}: true/false' to the file's frontmatter.`,
LINK_PREFIX_NAME: "Link prefix",
LINK_PREFIX_DESC: `${
"In PREVIEW mode, if the Text Element contains a link, precede the text with these characters. " +
"You can override this setting for a specific drawing by adding '"
}${FRONTMATTER_KEY_CUSTOM_PREFIX}: "📍 "' to the file's frontmatter.`,
URL_PREFIX_NAME: "URL prefix",
URL_PREFIX_DESC: `${
"In PREVIEW mode, if the Text Element contains a URL link, precede the text with these characters. " +
"You can override this setting for a specific drawing by adding '"
}${FRONTMATTER_KEY_CUSTOM_URL_PREFIX}: "🌐 "' to the file's frontmatter.`,
LINK_CTRL_CLICK_NAME:
"CTRL/CMD + CLICK on text with [[links]] or [](links) to open them",
LINK_CTRL_CLICK_DESC:
"You can turn this feature off if it interferes with default Excalidraw features you want to use. If " +
"this is turned off, only the link button in the title bar of the drawing pane will open links.",
TRANSCLUSION_WRAP_NAME: "Overflow wrap behavior of transcluded text",
TRANSCLUSION_WRAP_DESC:
"Number specifies the character count where the text should be wrapped. " +
"Set the text wrapping behavior of transcluded text. Turn this ON to force-wrap " +
"text (i.e. no overflow), or OFF to soft-wrap text (at the nearest whitespace).",
PAGE_TRANSCLUSION_CHARCOUNT_NAME: "Page transclusion max char count",
PAGE_TRANSCLUSION_CHARCOUNT_DESC:
"The maximum number of characters to display from the page when transcluding an entire page with the " +
"![[markdown page]] format.",
GET_URL_TITLE_NAME: "Use iframely to resolve page title",
GET_URL_TITLE_DESC:
"Use the http://iframely.server.crestify.com/iframely?url= to get title of page when dropping a link into Excalidraw",
MD_HEAD: "Markdown-embed settings",
MD_HEAD_DESC:
"You can transclude formatted markdown documents into drawings as images CTRL/CMD drop from the file explorer or using " +
"the command palette action.",
MD_TRANSCLUDE_WIDTH_NAME: "Default width of a transcluded markdown document",
MD_TRANSCLUDE_WIDTH_DESC:
"The width of the markdown page. This effects the word wrapping when transcluding longer paragraphs, and the width of " +
"the image element. You can override the default width of " +
"an embedded file using the [[filename#heading|WIDTHxMAXHEIGHT]] syntax in markdown view mode under embedded files.",
MD_TRANSCLUDE_HEIGHT_NAME:
"Default maximum height of a transcluded markdown document",
MD_TRANSCLUDE_HEIGHT_DESC:
"The embedded image will be as high as the markdown text requries, but not higher than this value. " +
"You can override this value by editing the embedded image link in markdown view mode with the following syntax [[filename#^blockref|WIDTHxMAXHEIGHT]].",
MD_DEFAULT_FONT_NAME:
"The default font typeface to use for embedded markdown files.",
MD_DEFAULT_FONT_DESC:
'Set this value to "Virgil" or "Cascadia" or the filename of a valid .ttf, .woff, or .woff2 font e.g. "MyFont.woff2" ' +
'You can override this setting by adding the following frontmatter-key to the embedded markdown file: "excalidraw-font: font_or_filename"',
MD_DEFAULT_COLOR_NAME:
"The default font color to use for embedded markdown files.",
MD_DEFAULT_COLOR_DESC:
'Set this to allowed css color names e.g. "steelblue" (https://www.w3schools.com/colors/colors_names.asp), or a valid hexadecimal color e.g. "#e67700". ' +
'You can override this setting by adding the following frontmatter-key to the embedded markdown file: "excalidraw-font-color: color_name_or_rgbhex"',
MD_CSS_NAME: "CSS file",
MD_CSS_DESC:
"The filename of the CSS to apply to markdown embeds. Provide the filename with extension (e.g. 'md-embed.css'). The css file may also be a plain " +
"markdown file (e.g. 'md-embed-css.md'), just make sure the content is written using valid css syntax. " +
"If you need to look at the HTML code you are applying the CSS to, then open Obsidian Developer Console (CTRL+SHIFT+i) and type in the follwoing command: " +
'"ExcalidrawAutomate.mostRecentMarkdownSVG". This will display the most recent SVG generated by Excalidraw. ' +
"Setting the font-family in the css is has limitations. By default only your operating system's standard fonts are available (see README for details). " +
"You can add one custom font beyond that using the setting above. " +
'You can override this css setting by adding the following frontmatter-key to the embedded markdown file: "excalidraw-css: css_file_in_valut|css-snippet".',
EMBED_HEAD: "Embed & Export",
EMBED_PREVIEW_SVG_NAME: "Display SVG in markdown preview",
EMBED_PREVIEW_SVG_DESC:
"The default is to display drawings as SVG images in the markdown preview. Turning this feature off, the markdown preview will display the drawing as an embedded PNG image.",
PREVIEW_MATCH_OBSIDIAN_NAME: "Excalidraw preview to match Obsidian theme",
PREVIEW_MATCH_OBSIDIAN_DESC:
"Image preview in documents should match the Obsidian theme. If enabled, when Obsidian is in dark mode, Excalidraw images will render in dark mode. " +
"When Obsidian is in light mode, Excalidraw will render light mode as well. You may want to switch 'Export image with background' off for a more Obsidian-integrated look and feel.",
EMBED_WIDTH_NAME: "Default width of embedded (transcluded) image",
EMBED_WIDTH_DESC:
"Only relevant if embed type is excalidraw. Has no effect on PNG and SVG embeds. The default width of an embedded drawing. You can specify a custom " +
"width when embedding an image using the ![[drawing.excalidraw|100]] or " +
"[[drawing.excalidraw|100x100]] format.",
EMBED_TYPE_NAME: "Type of file to insert into the document",
EMBED_TYPE_DESC:
"When you embed an image into a document using the command palette this setting will specify if Excalidraw should embed the original excalidraw file " +
"or a PNG or an SVG copy. You need to enable auto-export PNG / SVG (see below under Export Settings) for those image types to be available in the dropdown. For drawings that do not have a " +
"a corresponding PNG or SVG readily available the command palette action will insert a broken link. You need to open the original drawing and initiate export manually. " +
"This option will not autogenerate PNG/SVG files, but will simply reference the already existing files.",
EXPORT_PNG_SCALE_NAME: "PNG export image scale",
EXPORT_PNG_SCALE_DESC: "The size-scale of the exported PNG image",
EXPORT_BACKGROUND_NAME: "Export image with background",
EXPORT_BACKGROUND_DESC:
"If turned off, the exported image will be transparent.",
EXPORT_THEME_NAME: "Export image with theme",
EXPORT_THEME_DESC:
"Export the image matching the dark/light theme of your drawing. If turned off, " +
"drawings created in dark mode will appear as they would in light mode.",
EXPORT_HEAD: "Export Settings",
EXPORT_SYNC_NAME:
"Keep the .SVG and/or .PNG filenames in sync with the drawing file",
EXPORT_SYNC_DESC:
"When turned on, the plugin will automatically update the filename of the .SVG and/or .PNG files when the drawing in the same folder (and same name) is renamed. " +
"The plugin will also automatically delete the .SVG and/or .PNG files when the drawing in the same folder (and same name) is deleted. ",
EXPORT_SVG_NAME: "Auto-export SVG",
EXPORT_SVG_DESC:
"Automatically create an SVG export of your drawing matching the title of your file. " +
"The plugin will save the *.SVG file in the same folder as the drawing. " +
"Embed the .svg file into your documents instead of excalidraw making you embeds platform independent. " +
"While the auto-export switch is on, this file will get updated every time you edit the excalidraw drawing with the matching name.",
EXPORT_PNG_NAME: "Auto-export PNG",
EXPORT_PNG_DESC: "Same as the auto-export SVG, but for *.PNG",
COMPATIBILITY_HEAD: "Compatibility features",
EXPORT_EXCALIDRAW_NAME: "Auto-export Excalidraw",
EXPORT_EXCALIDRAW_DESC: "Same as the auto-export SVG, but for *.Excalidraw",
SYNC_EXCALIDRAW_NAME:
"Sync *.excalidraw with *.md version of the same drawing",
SYNC_EXCALIDRAW_DESC:
"If the modified date of the *.excalidraw file is more recent than the modified date of the *.md file " +
"then update the drawing in the .md file based on the .excalidraw file",
COMPATIBILITY_MODE_NAME: "New drawings as legacy files",
COMPATIBILITY_MODE_DESC:
"By enabling this feature drawings you create with the ribbon icon, the command palette actions, " +
"and the file explorer are going to be all legacy *.excalidraw files. This setting will also turn off the reminder message " +
"when you open a legacy file for editing.",
EXPERIMENTAL_HEAD: "Experimental features",
EXPERIMENTAL_DESC:
"These setting will not take effect immediately, only when the File Explorer is refreshed, or Obsidian restarted.",
FILETYPE_NAME: "Display type (✏️) for excalidraw.md files in File Explorer",
FILETYPE_DESC:
"Excalidraw files will receive an indicator using the emojii or text defined in the next setting.",
FILETAG_NAME: "Set the type indicator for excalidraw.md files",
FILETAG_DESC: "The text or emojii to display as type indicator.",
INSERT_EMOJI: "Insert an emoji",
LIVEPREVIEW_NAME: "Immersive image embedding in live preview editing mode",
LIVEPREVIEW_DESC: "Turn this on to support image embedding styles such as ![[drawing|width|style]] in live preview editing mode. " +
"The setting will not effect the currently open documents. You need close the open documents and re-open them for the change " +
"to take effect.",
//openDrawings.ts
SELECT_FILE: "Select a file then press enter.",
NO_MATCH: "No file matches your query.",
SELECT_FILE_TO_LINK: "Select the file you want to insert the link for.",
SELECT_DRAWING: "Select the drawing you want to insert",
TYPE_FILENAME: "Type name of drawing to select.",
SELECT_FILE_OR_TYPE_NEW:
"Select existing drawing or type name of a new drawing then press Enter.",
SELECT_TO_EMBED: "Select the drawing to insert into active document.",
SELECT_MD: "Select the markdown document you want to insert",
//EmbeddedFileLoader.ts
INFINITE_LOOP_WARNING:
"EXCALIDRAW WARNING\nAborted loading embedded images due to infinite loop in file:\n",
};

3
src/lang/locale/es.ts Normal file
View File

@@ -0,0 +1,3 @@
// Español
export default {};

3
src/lang/locale/fr.ts Normal file
View File

@@ -0,0 +1,3 @@
// français
export default {};

3
src/lang/locale/hi.ts Normal file
View File

@@ -0,0 +1,3 @@
// हिन्दी
export default {};

Some files were not shown because too many files have changed in this diff Show More