Compare commits

...

489 Commits

Author SHA1 Message Date
zsviczian
bb83523c0f fix script loading error 2024-11-04 12:29:20 +00:00
zsviczian
f83c0a8458 Merge pull request #2097 from dmscode/master
Update zh-cn.ts to 55ce645
2024-11-04 07:58:33 +01:00
dmscode
7411d51477 Update zh-cn.ts to 55ce645 2024-11-04 07:39:41 +08:00
zsviczian
55ce6456d8 2.6.4 2024-11-03 17:56:39 +01:00
zsviczian
da6619d55e 2.6.3 2024-11-03 15:07:11 +01:00
zsviczian
6033c057c2 2.6.3-beta-6 2024-11-03 13:32:18 +01:00
zsviczian
0efda1d6a6 2.6.3-beta-5 2024-11-03 00:54:38 +01:00
zsviczian
59107f0c2a 2.6.3-beta-4 2024-11-02 20:05:13 +01:00
zsviczian
f7cd05f6c4 2.6.3-beta-3 (refactored initiation) 2024-11-02 07:50:27 +01:00
zsviczian
5cbd98e543 Merge pull request #2092 from dmscode/master
Update zh-cn.ts  to dec2909
2024-11-02 07:45:49 +01:00
dmscode
e2d5966ca3 Update zh-cn.ts to dec2909 2024-11-01 18:37:48 +08:00
zsviczian
dec2909db0 2.6.2-beta-2, 0.17.6-10 PDFCropping 2024-11-01 07:44:58 +01:00
zsviczian
7233d1e037 2.6.3-beta-1 2024-10-30 23:02:05 +01:00
zsviczian
5972f83369 Merge pull request #2083 from dmscode/master
Update zh-cn.ts to 8f14f97
2024-10-30 22:08:28 +01:00
dmscode
0edfd7622c Update zh-cn.ts to 8f14f97 2024-10-29 07:36:30 +08:00
zsviczian
8f14f97007 2.6.2 2024-10-28 22:12:25 +01:00
zsviczian
758585a4c2 2.6.1 2024-10-28 20:26:57 +01:00
zsviczian
854eafaf91 2.6.0 2024-10-27 15:57:25 +01:00
zsviczian
ee89b80ce1 Merge pull request #2079 from dmscode/master
Update zh-cn.ts to ee9364b
2024-10-27 07:12:14 +01:00
dmscode
3e6200ac7e Update zh-cn.ts to ee9364b 2024-10-27 06:37:38 +08:00
zsviczian
ee9364b645 2.6.0-beta-4 2024-10-26 14:41:10 +02:00
zsviczian
5bbe66900e 2.6.0-beta-3 2024-10-26 13:53:08 +02:00
zsviczian
a775a858c7 2.6.0-beta-2 2024-10-26 08:39:28 +02:00
zsviczian
2dab801ff5 Merge pull request #2078 from dmscode/master
Update zh-cn.ts to 91be6e2
2024-10-26 08:11:17 +02:00
dmscode
07f8a87580 Update zh-cn.ts to 91be6e2 2024-10-26 07:41:49 +08:00
zsviczian
91be6e2a2f local cjk fonts 2024-10-25 23:54:01 +02:00
zsviczian
5c709588dd 2.6.0-beta-1, 0.17.6-6, embedded file loader batching 2024-10-23 22:23:24 +02:00
zsviczian
19a46e5b11 2.3.5-beta-5 2024-10-23 06:43:56 +02:00
zsviczian
e132d4a9fc 2.5.3-beta-4 improved loading speeds, image cropping 2024-10-22 20:44:17 +02:00
zsviczian
cf2d9bea24 2.5.3-beta-3 2024-10-21 21:17:44 +02:00
zsviczian
09cbffed1e 2.5.3-beta-2 2024-10-20 21:38:33 +02:00
zsviczian
368de8c1f4 Merge pull request #2070 from tovBender/master
Update ru.ts
2024-10-20 21:21:20 +02:00
zsviczian
7dcf9173c2 Merge pull request #2071 from dmscode/master
Update zh-cn.ts to 7cac94b
2024-10-20 21:19:32 +02:00
dmscode
eac312c3a2 Update zh-cn.ts to 7cac94b 2024-10-20 10:11:58 +08:00
tovBender
7a420a9d2d Update ru.ts 2024-10-19 22:18:52 +03:00
zsviczian
7cac94bf2f 2.5.3-beta-1 2024-10-19 20:43:48 +02:00
zsviczian
43e98db174 remove font assets, replace with zip 2024-10-19 16:21:20 +02:00
zsviczian
253575bf23 font assets 2024-10-19 16:11:24 +02:00
zsviczian
7a08ced65a Merge pull request #2066 from heinrich26/master
Make the Tab Icons color change as well, if a Tab is dirty (unsaved)
2024-10-19 06:40:18 +02:00
Hendrik Horstmann
5a64e1c75e Merge branch 'zsviczian:master' into master 2024-10-16 19:28:22 +02:00
zsviczian
fc0ac92dd3 2.5.2 2024-10-13 20:59:43 +02:00
zsviczian
4e2d7eb637 Update README.md 2024-09-28 22:32:18 +02:00
zsviczian
f8f280c7d5 2.5.1 2024-09-28 14:10:25 +02:00
zsviczian
00b87f99c0 Merge pull request #2038 from mxsdlr/master
Update color palette details in README
2024-09-25 17:14:56 +02:00
mxsdlr
0b5c74dde8 Add Decompress JSON hint to README.md
- Add hint to "Decompress Excalidraw JSON in Markdown View" setting when editing JSON content
2024-09-24 14:33:10 +02:00
mxsdlr
906b3bdf92 Update color palette details in README
- Change `customColorPalette` to `colorPalette`
- Add section about `topPicks`
2024-09-24 11:43:29 +02:00
zsviczian
0c28e82212 2.5.1-beta-2 2024-09-22 16:22:17 +02:00
zsviczian
beb4301f14 Merge pull request #2033 from dmscode/master
Update zh-cn.ts to 268680f
2024-09-22 15:21:11 +02:00
dmscode
e96fe9c491 Update zh-cn.ts to 268680f 2024-09-22 07:18:10 +08:00
zsviczian
268680f494 2.5.1-beta-1 2024-09-19 20:22:04 +02:00
zsviczian
a1512fce26 Merge pull request #2024 from dmscode/master
Update zh-cn.ts to 74c0af2
2024-09-19 20:08:58 +02:00
zsviczian
c2e79f3439 Update bug_report.yml 2024-09-14 11:25:36 +02:00
zsviczian
01780a2bf8 Update bug_report.yml 2024-09-14 11:24:44 +02:00
zsviczian
10e54eb03e Update bug_report.yml 2024-09-14 11:24:01 +02:00
zsviczian
2760a9966b Update bug_report.yml 2024-09-14 11:23:40 +02:00
dmscode
a297dbbe52 Update zh-cn.ts to 74c0af2
And fixed some link path
2024-09-14 08:09:10 +08:00
zsviczian
74c0af2032 2.5.0 2024-09-13 18:45:13 +02:00
zsviczian
813c85accd 2.5.0-rc-1 2024-09-13 08:18:19 +02:00
zsviczian
c97d08c997 Merge pull request #2017 from zsviczian/fix-getExcalidrawViews
fixed getExcalidrawView
2024-09-12 12:29:49 +02:00
zsviczian
097d1bcd1b fixed getExcalidrawView 2024-09-12 10:27:04 +00:00
zsviczian
7c91186ed5 Update ExcalidrawViewUtils.ts 2024-09-12 11:41:47 +02:00
zsviczian
904bc7c994 Update ExcalidrawView.ts 2024-09-12 11:39:25 +02:00
zsviczian
9fd4ae2615 Update manifest-beta.json 2024-09-12 11:16:51 +02:00
zsviczian
18fbb0934e Merge pull request #2016 from zsviczian/style-tweaks
Obsidian 1.7.2: rework getExcalidrawViews from leaves, fix tools pane…
2024-09-12 11:10:11 +02:00
zsviczian
3ae59c85d2 Obsidian 1.7.2: rework getExcalidrawViews from leaves, fix tools panel width, fix math types 2024-09-12 09:07:43 +00:00
zsviczian
55db9b0ddb 2.5.0-beta-3 2024-09-11 22:21:50 +02:00
zsviczian
3cca2fedb0 2.5.0-beta-2 2024-09-10 18:56:38 +02:00
zsviczian
a0682b8e3c Merge pull request #2014 from dmscode/master
Update zh-cn.ts to 2.4.3
2024-09-10 07:38:37 +02:00
dmscode
da19ce1b84 Update zh-cn.ts to 2.4.3
Add a folder in ./docs for more Chinese docs, and move zh-cn README into it. Translated AutomateHowTo.md
2024-09-10 07:54:46 +08:00
zsviczian
2b1f504b5b 2.5.0-beta-1, changed to Excalidraw.com search 2024-09-09 23:03:57 +02:00
zsviczian
d8f2776b40 Improved template load performance by filtering fileIds based on selection. 2024-09-09 21:27:39 +02:00
zsviczian
2fd800e0a0 2.4.3 2024-09-09 20:20:12 +02:00
zsviczian
712402f7b1 2.4.3-rc-2 2024-09-09 20:00:39 +02:00
zsviczian
b5c034ed92 2.4.3-rc-1 2024-09-08 21:16:11 +02:00
zsviczian
d2805e7a8a Rename feature_request.md to feature_request.md.old 2024-09-07 07:20:34 +02:00
zsviczian
4011b1c89a Rename feature-request-yml to feature-request.yml 2024-09-07 07:20:10 +02:00
zsviczian
5bc348611c Create feature-request-yml 2024-09-07 07:19:42 +02:00
zsviczian
dd24affdbb Create How-to.yml 2024-09-07 06:51:15 +02:00
zsviczian
d8542824a1 removed autodraw for pen and hardware eraser support scripts 2024-09-06 22:11:10 +02:00
zsviczian
c6bf24ed60 2.4.2 2024-09-06 19:33:16 +02:00
zsviczian
a910550e34 2.4.2-rc-1 2024-09-06 17:35:17 +02:00
zsviczian
01e53471d7 Merge pull request #1993 from dmscode/master
Update zh-cn.ts to 2.4.1
2024-09-06 13:56:48 +02:00
zsviczian
5569cff20e Update README.md 2024-09-05 18:15:07 +02:00
dmscode
757c12c386 Update zh-cn.ts to 2.4.1 2024-09-04 08:18:05 +08:00
zsviczian
4cfe6cecda Rename Contribution.yml to Contribution.yml.old 2024-09-03 20:02:53 +02:00
zsviczian
a34fbad038 Update Contribution.yml 2024-09-03 18:03:56 +02:00
zsviczian
6138ef03e0 Update Contribution.yml 2024-09-03 18:02:55 +02:00
zsviczian
9eb2821c9b Update Contribution.yml 2024-09-03 17:44:05 +02:00
zsviczian
628bd7bdb2 Update Contribution.yml 2024-09-03 17:41:08 +02:00
zsviczian
a3914da4ff Update Contribution.yml 2024-09-03 17:39:53 +02:00
zsviczian
6a1e7f08f4 Create Contribution.yml 2024-09-03 17:36:32 +02:00
zsviczian
15b400a3d9 2.4.1 2024-09-03 13:11:22 +02:00
zsviczian
e447cc3b67 2.4.0 2024-09-03 11:36:13 +02:00
zsviczian
8240c7e240 2.4.1-rc-1 2024-09-03 11:32:23 +02:00
zsviczian
6f020e9574 Merge pull request #1987 from dmscode/master
Fix
2024-09-03 10:13:32 +02:00
dmscode
c9d5f74ed4 Fix
-I found that the operation to modify custom brushes is a double-click, not a long press;
- Added a website to find free commercial Chinese fonts.
2024-09-03 10:06:31 +08:00
zsviczian
78034f6dea svg 2024-09-02 19:30:45 +02:00
zsviczian
b81cb52614 svg change 2024-09-02 19:25:02 +02:00
zsviczian
56280cd893 reset latex size 2024-09-02 19:02:09 +02:00
zsviczian
e515586e6b Merge pull request #1984 from firai/reset-latex-ea-script
Add script to reset the sizes of LaTeX equations
2024-09-02 11:11:47 +02:00
firai
424af4c60d Add Reset LaTeX Size script 2024-09-02 02:18:52 +08:00
zsviczian
0a7048aca1 update golden ratio 2024-09-01 09:03:34 +02:00
zsviczian
dd812e0684 update golden ratio script 2024-09-01 09:01:11 +02:00
zsviczian
51b7aebbc3 2.4.0 2024-08-31 13:37:27 +02:00
zsviczian
01b1698934 Update bug_report.yml 2024-08-30 14:10:42 +02:00
zsviczian
1025054bf4 Update bug_report.yml 2024-08-30 14:03:31 +02:00
zsviczian
06b4986997 Update bug_report.yml 2024-08-30 14:02:40 +02:00
zsviczian
31716ebcbc Update bug_report.yml 2024-08-30 13:58:45 +02:00
zsviczian
2e1f28f67e Update bug_report.yml 2024-08-30 13:56:20 +02:00
zsviczian
7f2e2b2d45 Update bug_report.yml 2024-08-30 13:52:52 +02:00
zsviczian
b961435e01 Update bug_report.yml 2024-08-30 13:48:09 +02:00
zsviczian
c54fab9603 Update bug_report.yml 2024-08-30 13:46:48 +02:00
zsviczian
cc6832afd6 Rename bug_report.md to bug_report.md.x 2024-08-30 13:44:13 +02:00
zsviczian
29c41cb45a Create bug_report.yml 2024-08-30 13:38:43 +02:00
zsviczian
c18984a26b Merge pull request #1977 from zsviczian/window-onblur
save on window blur
2024-08-30 13:20:30 +02:00
zsviczian
23d1ad0da6 save on window blur 2024-08-30 11:19:43 +00:00
zsviczian
49173dc766 reset autosave timer the first time the drawing changes 2024-08-30 12:49:00 +02:00
zsviczian
03a563856d Merge pull request #1974 from zsviczian/autosave-tweaks
autosave tweaks
2024-08-30 10:33:13 +02:00
zsviczian
c3809c409d autosave tweaks 2024-08-30 08:32:27 +00:00
zsviczian
dfdca90ca5 2.4.0-rc-2 2024-08-29 23:18:01 +02:00
zsviczian
6a8e1735db update nvmrc to 18 2024-08-29 20:29:54 +02:00
zsviczian
c0e9a0553e minor build changes 2024-08-29 20:24:59 +02:00
zsviczian
e1501165d9 Merge pull request #1971 from dmscode/master
Update zh-cn.ts to hotekey override, hide spalsh, save active tool, r…
2024-08-29 15:04:15 +02:00
dmscode
3b0f706059 Update zh-cn.ts to hotekey override, hide spalsh, save active tool, remove comments in p… … 2024-08-29 18:27:50 +08:00
zsviczian
7d19662f68 Merge pull request #1970 from zsviczian/hotkey-override
hotekey override, hide spalsh, save active tool, remove comments in p…
2024-08-29 11:58:30 +02:00
zsviczian
5c949dc71c hotekey override, hide spalsh, save active tool, remove comments in prod build 2024-08-29 09:57:53 +00:00
zsviczian
0439d67a0c Merge pull request #1966 from dmscode/master
Update zh-cn.ts to 2.4.0-rc-1
2024-08-29 09:00:59 +02:00
dmscode
d3446a20b1 Update zh-cn.ts to 2.4.0-rc-1 2024-08-28 20:45:21 +08:00
zsviczian
5b37dc2e38 save on contentEl mouseleave 2024-08-28 12:34:56 +02:00
zsviczian
eee264918e 2.4.0-rc-1 2024-08-27 21:43:21 +02:00
zsviczian
89172a88f1 fix android preview render bug 2024-08-27 08:56:57 +02:00
zsviczian
ffdb054291 2.4.0-beta-10 2024-08-26 22:40:45 +02:00
zsviczian
200d39c408 Merge pull request #1960 from dmscode/master
Update zh-cn.ts and add Readme.zh-cn
2024-08-26 22:27:07 +02:00
zsviczian
4306574ace Update Excalidraw Writing Machine 2024-08-26 15:04:58 +02:00
zsviczian
12e3b90458 support wikilinks 2024-08-26 15:04:05 +02:00
dmscode
03364b5d2e Readme.zh-cn 2024-08-26 15:43:28 +08:00
dmscode
4e268991dc Update zh-cn.ts to 2.4.0-beta-9 2024-08-26 14:32:46 +08:00
zsviczian
429c84f940 updated rollup, tsconfig, rollup versions in package.json 2024-08-25 16:53:15 +02:00
zsviczian
e890e4489b replace json.stringify with proper processing, fix small issues with Ephemral state, added worker (inactive) 2024-08-25 16:08:12 +02:00
zsviczian
8466c42217 update Excalidraw package -42 is corrupted 2024-08-24 20:30:51 +02:00
zsviczian
353732f597 2.4.0-beta-9 2024-08-24 13:59:49 +02:00
zsviczian
5599d2507f update writing machine data 2024-08-23 12:43:27 +02:00
zsviczian
70cf6ffe70 Support embeddables 2024-08-23 12:41:33 +02:00
zsviczian
61c9277097 writing machine 2024-08-22 21:59:06 +02:00
zsviczian
401052efd3 svg icon take 2 2024-08-22 20:36:00 +02:00
zsviczian
a57a0e797d update writing machine svg 2024-08-22 20:33:52 +02:00
zsviczian
8f48853e2c updated writing machine script 2024-08-22 17:58:42 +02:00
zsviczian
a62148dc07 publish Excalidraw Writing Machine 2024-08-21 21:53:59 +02:00
zsviczian
3ae890bd86 2.4.0-beta-8 2024-08-19 15:57:23 +02:00
zsviczian
65a4cd4ba5 Merge pull request #1939 from dmscode/master
Update zh-cn.ts 2.4.0-beta-7
2024-08-19 15:38:56 +02:00
dmscode
f63b473bc1 Update zh-cn.ts to the commit "fixed getTemplate, migrade gridSize, gridStep" 2024-08-19 09:42:37 +08:00
稻米鼠
859a5ba03a Merge branch 'zsviczian:master' into master 2024-08-19 09:37:07 +08:00
zsviczian
832b97b179 fixed getTemplate, migrade gridSize, gridStep, 2024-08-18 17:15:03 +02:00
dmscode
e98d688d36 Update zh-cn.ts 2.4.0-beta-7 2024-08-16 15:53:39 +08:00
zsviczian
39318337fe updated set stroke width script 2024-08-16 08:13:55 +02:00
zsviczian
f21215be84 2.4.0-beta-7 2024-08-15 17:45:49 +02:00
zsviczian
0690525af8 Merge pull request #1937 from dmscode/master
Update zh-cn.ts to 2.4.0-beta-6
2024-08-15 17:41:32 +02:00
dmscode
b3176425c5 Merge branch 'master' of https://github.com/dmscode/obsidian-excalidraw-plugin 2024-08-15 18:56:30 +08:00
dmscode
9f2c18b6b6 Update zh-cn.ts to 2.4.0-beta-6 2024-08-15 18:56:20 +08:00
zsviczian
d529a04f48 2.4.0-beta-6, pdf++ crop support, double tap eraser disable 2024-08-14 20:02:41 +02:00
zsviczian
8786c5aa99 2.4.0-beta-5 2024-08-14 10:07:55 +02:00
zsviczian
013279ab60 2.2.4-beta-4, markdown post processor, PDF frames, selectFrameElements 2024-08-13 22:53:03 +02:00
zsviczian
06193b6d49 2.4.0-beta-3 2024-08-11 09:22:13 +02:00
zsviczian
ac6f4af5d6 Merge pull request #1928 from mProjectsCode/patch-1
Fix `authorUrl` in manifest
2024-08-11 08:42:11 +02:00
zsviczian
bf148adc68 2.4.0-beta-2 2024-08-09 23:36:20 +02:00
Moritz Jung
0f9dafb01d Fix authorUrl in manifest 2024-08-09 22:16:07 +02:00
zsviczian
9fc0452b70 2.4.0-beta-1 2024-08-08 23:27:57 +02:00
zsviczian
83eda9b3f5 Update README.md 2024-08-04 11:01:12 +02:00
zsviczian
9bfbf47963 2.3.0 2024-08-04 10:37:06 +02:00
zsviczian
252bf411b1 2.3.0-beta-1 2024-08-03 21:26:47 +02:00
zsviczian
5622c019dd rebuild view DEVICE.isDesktop 2024-08-02 17:55:23 +02:00
zsviczian
b32fab7865 2.2.13-1 2024-08-01 22:18:16 +02:00
zsviczian
cafdad1f7a onWindowMigrated 2024-07-30 18:54:54 +02:00
zsviczian
9da40944ab 2.2.13 2024-07-29 18:13:13 +02:00
zsviczian
f678203a64 Merge pull request #1908 from dmscode/master
Update zh-cn.ts to 2.2.11
2024-07-29 17:32:14 +02:00
zsviczian
a9572e08e9 Merge branch 'master' into master 2024-07-29 17:31:58 +02:00
dmscode
ee9b042cdf Update zh-cn.ts to 2.2.11 2024-07-28 20:42:41 +08:00
zsviczian
bc138fa78a fixed storeAction, replaced "none" with "update" #1906 2024-07-28 10:06:31 +02:00
zsviczian
67dbe256f7 2.2.12 2024-07-28 06:55:27 +02:00
zsviczian
8b066d46e2 fix rename and skip inline fonts to cache key 2024-07-28 06:24:30 +02:00
zsviczian
1769a65a82 2.2.11 2024-07-27 21:25:23 +02:00
zsviczian
941eb56769 updated ExcaliAI 2024-07-27 07:09:19 +02:00
zsviczian
a317613ef4 Merge pull request #1859 from Saik0s/patch-1
Update ExcaliAI.md to fix incorrect request json for openai api
2024-07-27 07:05:58 +02:00
zsviczian
173571846f 2.2.10-2 2024-07-26 20:11:20 +02:00
zsviczian
ab1078d393 Fourth Font 2.2.10-1 2024-07-25 21:59:42 +02:00
zsviczian
681321a595 2.2.10-1 fontpicker beta 2024-07-25 21:52:47 +02:00
zsviczian
fcd50d4bc2 Merge pull request #1897 from dmscode/master
Updated zh-cn.ts to 2.2.10
2024-07-23 17:49:00 +02:00
dmscode
7e214e5aaa Updated zh-cn.ts to 2.2.10 2024-07-23 09:54:43 +08:00
zsviczian
de39053857 2.2.10 2024-07-22 22:01:36 +02:00
zsviczian
f543e3218e clippedframe, framesettings 2024-07-21 17:54:00 +02:00
zsviczian
e668aea214 Merge pull request #1890 from dmscode/master
Fixed link path
2024-07-20 08:33:09 +02:00
zsviczian
d810daa735 bump excalidraw version 2024-07-19 12:20:21 +02:00
zsviczian
b94b3118eb improved error message when getPNG fails 2024-07-19 12:19:48 +02:00
dmscode
06cbd0c92d Fixed link path 2024-07-19 12:44:50 +08:00
zsviczian
96ebbbf11d tolerate ! for embedded files 2024-07-17 21:05:01 +02:00
zsviczian
fc1467b05b Support for area, group, frame references in image embeds 2024-07-17 20:31:36 +02:00
zsviczian
53c27f2a59 2.2.9 2024-07-13 17:34:19 +02:00
zsviczian
db80f5c715 allows opacity 0% 2024-07-09 16:45:06 +02:00
zsviczian
405c98ca50 2.2.8 2024-07-05 07:00:42 +02:00
zsviczian
4892aed9e6 Merge pull request #1825 from dmscode/master
Updated and formated zh-cn.ts
2024-07-04 20:33:30 +02:00
zsviczian
1b04b94db2 Merge pull request #1843 from tobybaratta/patch-1
Documentation: Update Custom Pens documentation links
2024-07-04 20:33:01 +02:00
zsviczian
4a430f5fe7 pre-2.2.8 2024-07-04 20:15:33 +02:00
zsviczian
325bfd825f 2.2.7-5 beta 2024-07-04 09:23:49 +02:00
zsviczian
471553f913 2.2.7-4 2024-07-03 00:46:55 +02:00
zsviczian
a43d0689d8 2.2.7-3 without slob 2024-07-01 20:57:47 +02:00
zsviczian
afacb8a94b 2.2.7-2 2024-07-01 20:51:01 +02:00
zsviczian
afe5300e00 2.2.7-1 beta release after refactoring 2024-06-30 23:19:12 +02:00
zsviczian
ef85d0e323 select similar elements 2024-06-30 11:37:22 +02:00
Igor Tarasenko
d3aeedb9f6 Update ExcaliAI.md to fix incorrect request json for openai api 2024-06-28 21:03:58 +02:00
Toby Baratta
7718f69269 Fixing links to file names with spaces
commonmark spec thank you for working with spaces now
2024-06-20 19:17:00 -07:00
Toby Baratta
af50b0c5c6 Update Custom Pens documentation links
**Problem:**
- The README right now contains a broken link to a not-on-main file documenting how to add custom pens. (That document was archived -- I found the original via searching and tried to find the corresponding PRs for the details on the replacement functionality but couldn't find that PR.) 
- Pens are now handled in settings with a different YouTube video showing how to set them up (I think). 
- Adds links to the Hardware Eraser functionality & Auto Draw for Pen. 

Question:
- I think it might be useful to link to the freestyle docs here still, or have some sort of master "Alternative Pens" link that still shows how to do the JSON setup? Not sure if necessary.
2024-06-20 18:55:35 -07:00
dmscode
b576faad82 Updated zh-cn.ts to 2.2.7 2024-06-21 09:34:15 +08:00
稻米鼠
4583e603e9 Merge branch 'zsviczian:master' into master 2024-06-21 09:24:12 +08:00
zsviczian
7ee316a605 2.2.7 2024-06-19 20:56:34 +02:00
dmscode
7dca225691 Updated and formated zh-cn.ts
- Updated to 2.2.6
- Some punctuation to Chinese punctuation
- Add space between Chinese and English
- Change keyname to just first letter upper
- Remove space at line end
2024-06-13 08:23:22 +08:00
zsviczian
8a27d0240a 2.2.6 2024-06-12 22:23:32 +02:00
zsviczian
939bd6fd91 Merge pull request #1815 from dmscode/master
Update zh-cn.ts to 2.2.4
2024-06-12 22:20:20 +02:00
zsviczian
dfbd385de7 2.2.5 2024-06-09 16:34:48 +02:00
dmscode
0d791070dd Update zh-cn.ts to 2.2.4 2024-06-05 19:02:20 +08:00
zsviczian
94fbac38bf 2.2.4 2024-06-02 12:28:52 +02:00
zsviczian
73cf8e75d3 Merge pull request #1679 from evolutioned/patch-1
Update templater_mindmap.md grammar
2024-06-02 06:44:32 +02:00
zsviczian
395cbc104c Merge pull request #1805 from jmhammond/master
Remove autosaveInterval variable but preserve functionality
2024-06-02 06:43:31 +02:00
zsviczian
9a24db8379 Merge pull request #1810 from dmscode/master
Update zh-cn language to 2.2.3
2024-06-02 06:40:29 +02:00
zsviczian
934a5f4838 this to self 2024-06-02 06:39:55 +02:00
dmscode
9022500087 Update zh-cn language to 2.2.3 2024-06-01 12:03:23 +08:00
John Hammond
795807b6cf Remove autosaveInterval variable but preserve functionality 2024-05-29 17:15:14 -05:00
zsviczian
9eff79733c 2.2.3 2024-05-26 22:34:59 +02:00
zsviczian
c4e95d9207 replace commitToHistory with storeAction 2024-05-22 21:22:39 +02:00
zsviczian
32cd3a62b6 DEGUGGING in dev build with process.env.NODE_ENV, + this.lastSceneSnapshot 2024-05-22 20:08:49 +02:00
Hendrik Horstmann
a5771625df Make the Tab Icons color change as well, if a Tab is dirty (unsaved) 2024-05-22 16:17:30 +02:00
zsviczian
d05ccc0055 DEBUGGER improved 2024-05-20 19:28:33 +02:00
zsviczian
ff1d7b44b4 2.2.2 2024-05-20 12:25:53 +02:00
zsviczian
2b86ba2128 2.2.1 2024-05-20 09:37:14 +02:00
zsviczian
44a3b30e3b 2.2.0 2024-05-19 14:43:00 +02:00
zsviczian
bb9389c7dd cropping, fold everything, drag and drop fixes 2024-05-18 21:57:54 +02:00
zsviczian
ba4bfe9de7 embeddable theme 2024-05-18 14:33:25 +02:00
zsviczian
3a73b14ebb fixed select similar elements script 2024-05-18 09:35:42 +02:00
zsviczian
31f54db433 removed wrapAt, changed to refreshTextDimensions, updated onBeforeTextSubmit and Edit, replaced all != with !== plus additional type checks 2024-05-17 21:41:37 +02:00
zsviczian
26812dd297 force to open markdown command palette action, fixed decompress 2024-05-15 22:02:06 +02:00
zsviczian
ae4f4b4f08 2.1.8.1-beta-1 2024-05-14 21:07:42 +02:00
zsviczian
4ac0a4c565 2.1.8 2024-05-13 22:49:32 +02:00
zsviczian
69c9f824a0 Update README.md 2024-05-06 21:24:57 +02:00
zsviczian
6a2220c960 updated slideshow timestamp 2024-05-05 19:16:59 +02:00
zsviczian
aa501c2843 2.1.7 2024-05-05 19:13:30 +02:00
zsviczian
efce44f0a7 2.1.6.1-beta-1 2024-05-01 10:57:50 +02:00
zsviczian
491eb83d35 convert app.isMobile to DEVICE.isMobile, fix CropImage angle!==0 2024-04-30 19:31:00 +02:00
zsviczian
35cf0802d1 2.1.6 2024-04-23 22:19:49 +02:00
zsviczian
f768548f60 2.1.5 2024-04-22 20:10:21 +02:00
zsviczian
37789f9907 Update issue templates 2024-04-20 15:21:49 +02:00
zsviczian
131294464e 2.1.4 2024-04-13 09:44:15 +02:00
zsviczian
b7652a41f8 Update directory-info.json 2024-04-08 12:39:47 +02:00
zsviczian
91c5f85ec6 Update Deconstruct selected elements into new drawing.md 2024-04-08 12:38:40 +02:00
zsviczian
985983b31d updated slideshow script 2024-04-07 09:45:11 +02:00
zsviczian
9a27e38ce2 2.1.3 2024-04-06 15:52:34 +02:00
zsviczian
0017ed7c92 2.1.2 2024-04-06 13:32:56 +02:00
evolutioned
d5cf4ace21 Update templater_mindmap.md
Small grammar update
2024-04-02 06:40:55 +11:00
zsviczian
1c899746fd 2.1.1 2024-04-01 20:57:50 +02:00
zsviczian
9ea09c0fdd fixed excalidraw to PDF 2024-04-01 08:06:47 +02:00
zsviczian
cda674c289 2.1.0 2024-03-31 20:29:33 +02:00
zsviczian
ef7d7ccc91 2.0.26 2024-03-30 17:42:59 +01:00
zsviczian
21aa5eb2d6 wordsimthing... 2024-03-24 13:03:18 +01:00
zsviczian
e725fb9b65 improved description 2024-03-24 13:00:26 +01:00
zsviczian
fbd634bfce updated slideshow script 2024-03-24 12:53:53 +01:00
zsviczian
9a686f3827 corrected spelling in messages 2024-03-17 18:14:52 +01:00
zsviczian
999219a0c9 2.0.25 2024-03-17 18:01:18 +01:00
zsviczian
65fd370cc5 Revert "Merge pull request #1647 from blampewpew/feat-save-new-drawing-to-current-folder"
This reverts commit fa03968508, reversing
changes made to 78dace32d4.
2024-03-17 17:48:14 +01:00
zsviczian
232f0c38fa updated deconstruct script 2024-03-17 17:47:30 +01:00
zsviczian
fa03968508 Merge pull request #1647 from blampewpew/feat-save-new-drawing-to-current-folder
FR: Update Excalidraw Folder setting to allow saving drawings in the current folder or subfolder within current folder #1348
2024-03-17 17:14:12 +01:00
zsviczian
78dace32d4 support for templates folder 2024-03-17 16:57:10 +01:00
Bryan
a21e7aa0c5 Update helper text. 2024-03-16 15:13:57 -06:00
Bryan
9b7d828209 Set folder to current active folder if Excalidraw Folder name starts with "./" and embedUseExcalidrawFolder is true. 2024-03-16 15:13:45 -06:00
Bryan
a1071426c0 created getCurrentActiveDirectory helper function and updated reference in getAttachmentsFolderAndFilePath() 2024-03-16 15:09:53 -06:00
zsviczian
23baf21ef5 Merge pull request #1640 from zsviczian/custom-zoom
publish custom zoom script
2024-03-14 15:00:51 +01:00
zsviczian
d280b33073 publish custom zoom script 2024-03-14 14:58:39 +01:00
zsviczian
d1ab6bb7a1 2.0.24 2024-03-12 20:05:05 +01:00
zsviczian
146d04ea64 0.2.23 2024-02-25 16:15:32 +01:00
zsviczian
fd46a3f8ac Merge pull request #1602 from GColoy/Fix-Invert-Colors-Script
Fixed Issue with Invert Colors Script
2024-02-25 15:16:14 +01:00
zsviczian
9974ca1e2e 2.0.22 2024-02-21 18:49:19 +01:00
Felix
2c8a733359 Nested Arrays in colorPalette are taken into account 2024-02-20 19:54:30 +01:00
zsviczian
4c9eeb9a61 2.0.21 2024-02-19 19:32:44 +01:00
zsviczian
de23be8c2d Update directory-info.json 2024-02-19 08:03:22 +01:00
zsviczian
bb25a1fde9 Update ExcaliAI.md 2024-02-19 08:01:43 +01:00
zsviczian
9267c27bc5 Merge pull request #1578 from deining/fix-typos
Fix typos
2024-02-17 17:59:00 +01:00
zsviczian
591f073413 added match image fileId to Select Similar Elements 2024-02-12 17:53:04 +01:00
Andreas Deininger
9c28c1ee00 Fix typos 2024-02-05 01:27:36 +01:00
zsviczian
120d41ea2f 2.0.20 2024-02-04 10:46:16 +01:00
zsviczian
80b24a45ad fix getMaximumGroups 2024-02-04 10:36:24 +01:00
zsviczian
e8db9cbff6 2.0.19 2024-02-02 20:34:23 +01:00
zsviczian
5db4f8dd95 updated Crop Vintage Mask to ensure frame is at top 2024-01-29 22:55:27 +01:00
zsviczian
820b2ff6c4 2.0.18 2024-01-21 16:49:34 +01:00
zsviczian
596102fb68 publish crop vintage 2024-01-21 16:11:19 +01:00
zsviczian
fa5c63b224 publish crop script 2024-01-21 16:08:12 +01:00
zsviczian
aa66b8f716 Crop Vintage Mask Script 2024-01-21 15:41:57 +01:00
zsviczian
c06cdfcfa0 frogg image 2024-01-21 15:39:27 +01:00
zsviczian
e6943cf7f5 Merge pull request #1559 from BenMoon/patch-1
Fix typos
2024-01-21 11:44:26 +01:00
zsviczian
b3a2f067ab Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2024-01-21 11:43:28 +01:00
zsviczian
35407cf13d crop vintage mask image 2024-01-21 11:43:17 +01:00
BenMoon
d671faf712 Fix typos 2024-01-17 09:31:50 +01:00
zsviczian
815ad559ac updated deconstruct selected element into new drawing script 2024-01-16 15:06:12 +01:00
zsviczian
d2cf594756 Update Deconstruct selected elements into new drawing.md 2024-01-16 15:04:30 +01:00
zsviczian
6709f6cf87 updated text arch and split text by lines 2024-01-13 16:38:01 +01:00
zsviczian
f463f79222 updated split text by lines script 2024-01-11 21:15:25 +01:00
zsviczian
51cf3a9219 2.0.17 2024-01-10 22:12:02 +01:00
zsviczian
8700405af8 Merge pull request #1548 from tswwe/patch-4
Update zh-cn.ts
2024-01-10 20:51:15 +01:00
thxnder
d1d082b4f9 Update zh-cn.ts
keep up with en.ts
2024-01-10 20:20:04 +08:00
zsviczian
6dd9d1a056 2.0.16 2024-01-07 21:22:56 +01:00
zsviczian
46b03725e9 2.0.15 2024-01-07 10:57:53 +01:00
zsviczian
65d6577b28 Update README.md 2024-01-03 21:35:01 +01:00
zsviczian
97967f5b70 2.0.14 2024-01-03 16:32:07 +01:00
zsviczian
9323e1fad4 minor formatting change in rollup.config.json 2024-01-03 09:51:04 +01:00
zsviczian
3e4e741b54 2.0.13 2023-12-23 15:28:54 +01:00
zsviczian
4e2d8374e6 2.0.12 2023-12-23 11:02:57 +01:00
zsviczian
2b3037402a 2.0.11 2023-12-21 20:51:21 +01:00
zsviczian
bc67c27a82 updated ExcaliAI script 2023-12-21 19:49:15 +01:00
zsviczian
ffb8f6f00f css rework 2023-12-19 13:52:27 +01:00
zsviczian
d179dfe703 sliding panes support disabled 2023-12-18 17:26:28 +01:00
zsviczian
5701020901 Golden Ratio updated 2023-12-17 12:54:53 +01:00
zsviczian
1f2d795b58 stroke color currentColor GoldenRatio.svg 2023-12-17 09:36:54 +01:00
zsviczian
c123b3ef51 golden ratio svg 2023-12-17 07:57:13 +01:00
zsviczian
d565c7e9e9 Golden Ratio script 2023-12-17 07:54:15 +01:00
zsviczian
710a40c36b golden ratio image 2023-12-17 07:48:25 +01:00
zsviczian
64f9a5dd7d 2.0.10 2023-12-10 21:58:04 +01:00
zsviczian
323fe33c2f 2.0.9 2023-12-10 12:35:10 +01:00
zsviczian
2470f6f531 2.0.8 2023-12-08 17:19:16 +01:00
zsviczian
42968d8299 publishing relative font size cycle 2023-12-07 19:24:31 +01:00
zsviczian
7a50b6c77e Merge pull request #1425 from soraliu/master
feat(scripts): support repeat texts
2023-12-07 18:18:46 +01:00
zsviczian
0a10d5fbc9 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2023-12-06 18:14:47 +01:00
zsviczian
40d2345501 LaTeX Cleanup 2023-12-06 18:14:44 +01:00
zsviczian
ab97ae5ebc updated modifier key combinations table 2023-12-04 17:25:10 +01:00
zsviczian
7c93e90fc0 2.0.7 2023-12-04 14:49:33 +01:00
zsviczian
d44cf3306b 2.0.6 manifest 2023-12-04 08:44:30 +01:00
zsviczian
324609999f rollback 2023-12-03 23:35:03 +01:00
zsviczian
3f54b851ae updated grid selected images 2023-12-03 20:17:22 +01:00
zsviczian
8bbb04b421 Merge pull request #1465 from 7flash/patch-6
Update Grid Selected Images.md
2023-12-03 20:13:04 +01:00
zsviczian
dc396c8707 fix file utils var 2023-12-03 20:11:22 +01:00
zsviczian
52cc5d3aa7 2.0.5 2023-12-03 20:00:47 +01:00
zsviczian
87b6335905 cleaned out React.createElement 2023-12-01 20:45:37 +01:00
zsviczian
17358f16c8 fix getFileFromURL, remove observers when not needed 2023-12-01 17:21:41 +01:00
zsviczian
23eb268031 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2023-11-29 17:28:12 +01:00
zsviczian
27f4cb248d better error handling in ExcaliAI 2023-11-29 17:26:07 +01:00
zsviczian
bbaf4f7a34 updated ExcaliAI and GPT-Draw-a-UI timestamps 2023-11-26 19:59:39 +01:00
zsviczian
559455bf5b fix element.src to element.link 2023-11-26 19:57:42 +01:00
zsviczian
bd519aff08 fixed element.src to element.link 2023-11-26 19:57:04 +01:00
zsviczian
febeb787b5 Address undefined after installing the new version 2023-11-26 18:51:22 +01:00
zsviczian
9f8a9bfa8a 2.0.4 2023-11-26 18:38:31 +01:00
zsviczian
8b1daed0ef excaliAI 2023-11-26 16:06:55 +01:00
zsviczian
44c828c7e7 corrected minor error 2023-11-25 07:06:14 +01:00
Igor Berlenko
afabeaa2f3 Update Grid Selected Images.md 2023-11-24 22:41:51 +08:00
zsviczian
e72c1676c2 2.0.3 2023-11-21 22:51:56 +01:00
zsviczian
5a17eb7054 updated generator 2023-11-21 06:05:19 +01:00
zsviczian
75d52c07b8 lint 2023-11-20 23:05:55 +01:00
zsviczian
4dc6c17486 updated script 2023-11-20 22:32:13 +01:00
zsviczian
e780930799 draw a UI 2023-11-20 21:27:40 +01:00
zsviczian
49cd6a36a1 draw-a-ui-image 2023-11-20 21:20:12 +01:00
zsviczian
d4830983e2 2.0.2 2023-11-19 17:41:41 +01:00
zsviczian
a69fefffdc beta-2 2023-11-19 08:42:41 +01:00
zsviczian
1d0466dae7 2.0.1-beta-1 2023-11-18 17:43:47 +01:00
zsviczian
6e5a853d0f 2.0.2-beta-1 2023-11-18 17:40:59 +01:00
zsviczian
0c702ddf7b Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2023-11-18 17:16:26 +01:00
zsviczian
fdbffce1f9 Embeddable Settings 2023-11-18 17:16:24 +01:00
zsviczian
2872b4e3ce Merge pull request #1441 from heinrich26/patch-1
Fixed some typos
2023-11-18 17:14:45 +01:00
Hendrik Horstmann
0ba55e51e9 Fixed some typos 2023-11-15 10:50:16 +01:00
zsviczian
5887bf377d 2.0.1 2023-11-14 20:35:47 +01:00
zsviczian
c440dd9cf0 2.0.0 2023-11-13 22:16:43 +01:00
zsviczian
21bc1f7fa6 Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2023-11-12 17:29:40 +01:00
zsviczian
4279b13554 fix svg export 2023-11-12 17:29:37 +01:00
zsviczian
0e106b7c7b Merge pull request #1429 from 7flash/patch-5
Update Slideshow.md
2023-11-12 10:59:33 +01:00
zsviczian
4d7d1fba3a Image cache initialization watchdog 2023-11-12 09:43:34 +01:00
zsviczian
a35ea5e9da Adding the Assistant font, dynamic styling improvements, frame export colors, Removing ExcalidrawRef 2023-11-12 07:19:09 +01:00
zsviczian
48466f624d 1.9.29 beta 2023-11-11 11:57:23 +01:00
Igor Berlenko
f306b20449 Update Slideshow.md 2023-11-11 18:28:57 +08:00
zsviczian
fc4fd685ba publish support 2023-11-11 07:21:14 +01:00
zsviczian
7449df6ac6 docs 2023-11-09 19:00:03 +01:00
zsviczian
1390333c4c publish EA.d.ts to docs 2023-11-09 18:58:24 +01:00
zsviczian
a9f545a1b2 updated docs 2023-11-09 18:57:08 +01:00
Sora Liu
f80a96c703 feat(scripts): support repeat texts 2023-11-08 17:11:55 +04:00
zsviczian
f291c15bbc restructured render Excalidraw, support for cssclass, context menu 2023-11-06 20:37:53 +01:00
zsviczian
18821e1a67 1.9.28 2023-11-04 14:28:30 +01:00
zsviczian
5dd65d691c mermaid to retain SVG for legacy, LASER-config, PDF import improvements, image link click improvements, hover on image with link, dynamic styling fix, image cache fix 2023-11-03 23:02:09 +01:00
zsviczian
8f96dbc21d 1.9.27 2023-10-28 20:51:55 +02:00
zsviczian
f71623f8a1 1.9.26 2023-10-25 18:55:18 +02:00
zsviczian
b380420cac Update README.md 2023-10-23 07:50:52 +02:00
zsviczian
21cccd4475 updated readme 2023-10-23 07:50:10 +02:00
zsviczian
06475aea78 1.9.25 2023-10-22 21:15:32 +02:00
zsviczian
84af0c2d5c Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2023-10-15 17:14:41 +02:00
zsviczian
84648f5a56 1.9.24 2023-10-15 17:14:39 +02:00
zsviczian
60b7988860 fixed keyboard event handler when switching back and forth between views 2023-10-12 22:12:26 +02:00
zsviczian
732b7ad424 updated the slideshow script with the laserpointer 2023-10-07 21:44:06 +02:00
zsviczian
a664c34418 bump excalidraw package 2023-10-07 18:56:04 +02:00
zsviczian
eaf3b7f7d7 1.9.23 2023-10-07 18:47:33 +02:00
zsviczian
03edbaf545 1.9.22 2023-10-07 14:22:07 +02:00
zsviczian
c3edf23023 Merge pull request #1354 from LiamSwayne/patch-1
Grammar fix
2023-10-07 14:09:34 +02:00
Liam Swayne
5f765609f9 grammar fix 2023-10-04 13:47:07 -04:00
zsviczian
3c1beac822 added image 2023-10-01 17:55:33 +02:00
zsviczian
f30f66969c concatenate lines script 2023-10-01 17:48:43 +02:00
zsviczian
4e6fb48bf0 1.9.20 2023-09-30 22:27:16 +02:00
zsviczian
53225ba5a7 Merge pull request #1338 from Aeases/master
Fix Excalidraw Pasting with middle mouse button on linux
2023-09-30 18:41:34 +02:00
zsviczian
c04967d775 should render mermaid and disabling ctrl hover preview when object snapping 2023-09-30 18:41:11 +02:00
zsviczian
6bb7a3c0e5 publish boolean operations 2023-09-30 18:31:42 +02:00
zsviczian
2c85191671 mermaid 2 2023-09-29 19:22:22 +02:00
zsviczian
637235eb9d mermaid-2 beta 2023-09-29 19:16:38 +02:00
zsviczian
13e4203c44 Merge pull request #1346 from Raboro/patch-1
fixed typos & syntax errors
2023-09-26 18:49:21 +02:00
Marius Wörfel
d04b628d55 fixed typos & syntax errors 2023-09-25 22:16:57 +02:00
zsviczian
2eab13661d Merge pull request #1341 from GColoy/boolean-Operations
Boolean operations
2023-09-24 16:32:39 +02:00
GColoy
6eefb49eb0 Added version requirement to Boolean operations 2023-09-23 01:14:25 +02:00
GColoy
6878b1f969 added description and index entries 2023-09-23 01:07:58 +02:00
GColoy
0ba784564f replaced ShadowClone Propertie with group id 2023-09-23 00:07:33 +02:00
GColoy
62804cdc63 Boolean Operations Icon 2023-09-22 21:56:49 +02:00
GColoy
ff900b4b61 account for unclosed Lines 2023-09-22 21:56:34 +02:00
GColoy
e34f03c0d1 updated boolean Operations for new api 2023-09-20 21:25:40 +02:00
Felix
2a9cf1cf6b Merge branch 'zsviczian:master' into boolean-Operations 2023-09-20 21:24:09 +02:00
GColoy
d4159dbe75 Corrected Polybool import 2023-09-20 21:23:28 +02:00
GColoy
7cd68304c3 added Boolean Operations.md 2023-09-19 19:00:20 +02:00
GColoy
0d7a76310c added polybool library 2023-09-16 21:06:34 +02:00
zsviczian
056be574ef mermaid beta 2023-09-16 14:30:49 +02:00
zsviczian
f6e2886185 added basic mermaid support 2023-09-16 14:24:04 +02:00
Aeases
7b178ce2c8 Fix Excalidraw Pasting with middle mouse button on linux 2023-09-16 20:02:16 +08:00
zsviczian
e4d05ac284 added otf, fixed replaceSVGColors null 2023-09-12 18:28:14 +02:00
zsviczian
5311f53612 Merge pull request #1318 from tswwe/patch-3
Update zh-cn.ts
2023-09-09 09:58:10 +02:00
thxnder
f20ce396f0 Update zh-cn.ts
Keep up with en.ts
2023-09-09 12:26:23 +08:00
zsviczian
996f1f79f1 update index-new with deconstruct video link 2023-09-03 11:30:28 +02:00
zsviczian
cd58d1af06 1.9.19 2023-09-03 11:27:04 +02:00
zsviczian
9d9201b4d1 text aura 2023-09-03 11:03:24 +02:00
zsviczian
684b2f6268 text aura image 2023-09-03 10:02:48 +02:00
zsviczian
de08f4584d silent create - EA 2023-09-03 07:46:32 +02:00
zsviczian
a937b5d70a 1.9.18 2023-08-27 22:22:05 +02:00
zsviczian
95b99edede implemented editor-paste event listner 2023-08-27 15:13:45 +02:00
zsviczian
cdf7591e0f publis ellipse script 2023-08-27 13:03:52 +02:00
zsviczian
f373867356 Merge pull request #1293 from GColoy/Split-Ellipse
ea-script: Split ellipse
2023-08-27 13:00:27 +02:00
zsviczian
041efcab74 grid color now sets two colors in appstate 2023-08-27 07:18:11 +02:00
zsviczian
e1fe3eeaab fixed constants.ts filename casing issue. Updated tsconfig and rollout.config 2023-08-26 11:02:00 +02:00
GColoy
3793148c77 added index entries for split ellipse 2023-08-22 01:02:06 +02:00
GColoy
5dbfeb085e added split ellipse script and images 2023-08-22 01:01:50 +02:00
zsviczian
f79181c76a fix embed quote 2023-08-20 07:09:35 +02:00
zsviczian
c0df46cb7b 1.9.17 2023-08-19 20:23:59 +02:00
zsviczian
aa7dcf7604 fix image cache, fix ea error, insert quote 2023-08-16 06:32:51 +02:00
zsviczian
fe4a39afc5 toggle grid 2023-08-15 20:50:33 +02:00
zsviczian
4185192954 toggle grid 2023-08-15 20:41:27 +02:00
zsviczian
3a9ee63c97 Merge pull request #1280 from GColoy/ToggleGrid
Toggle grid ea-script
2023-08-15 20:38:08 +02:00
GColoy
d2d2537867 Added Index entrys 2023-08-15 16:29:19 +02:00
GColoy
97fe819737 added ToggleGrid script 2023-08-15 16:09:51 +02:00
zsviczian
9fdca28579 1.9.16 2023-08-11 17:30:31 +02:00
zsviczian
261e093700 1.9.15 2023-08-10 19:18:24 +02:00
zsviczian
07a651c2c8 update 2023-08-10 18:49:03 +02:00
zsviczian
6c0a1f9a4d index-new update 2023-08-10 18:47:18 +02:00
zsviczian
9d941a4e44 updated index-new.md 2023-08-10 18:45:37 +02:00
zsviczian
cbb8f676af implemented script store search 2023-08-09 19:59:16 +02:00
zsviczian
3bcf460ce4 styles manager improvements 2023-08-08 20:00:37 +02:00
zsviczian
23dd4883e3 semi translucent background for embeddables 2023-08-07 21:43:40 +02:00
zsviczian
ce2e0fd408 stylesManager 2023-08-07 20:31:51 +02:00
zsviczian
9438031b4a 1.9.14 2023-08-06 15:10:10 +02:00
zsviczian
d5e584c1f0 fixed save on workspace click, updated lib, updated packages 2023-08-06 10:00:12 +02:00
zsviczian
8624671c4c Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2023-08-06 08:21:36 +02:00
zsviczian
88094c056c before npm audit fix 2023-08-06 08:21:26 +02:00
zsviczian
fa918e1c76 publish select similar elements script 2023-08-05 23:33:59 +02:00
zsviczian
d89d35e420 publish select similar elements script image 2023-08-05 23:25:58 +02:00
zsviczian
ddcfddd698 1.9.13 2023-08-05 15:24:07 +02:00
zsviczian
bdce2477c3 Native SVG support 2023-08-03 21:57:11 +02:00
zsviczian
83aa04396c Support Templater scripts in Embeddable Markdown Documents 2023-08-02 22:18:43 +02:00
zsviczian
c9c5468fe4 allowFrameButtonsInViewMode 2023-08-02 16:58:41 +02:00
zsviczian
4d6ec72717 added video link to organic line script description 2023-07-29 07:22:44 +02:00
zsviczian
b8ce29214f index-new update 2023-07-29 07:14:09 +02:00
zsviczian
66197e81b3 publishing Organic Line Legacy 2023-07-29 07:11:35 +02:00
zsviczian
d925ece80a updated slideshow frame sorting 2023-07-27 22:49:40 +02:00
zsviczian
8b3c61ae24 1.9.12 2023-07-27 10:45:06 +02:00
zsviczian
3f0086359a sword 2023-07-27 10:14:24 +02:00
zsviczian
9a807e4f8a 1.9.11 release message 2023-07-26 15:54:24 +02:00
zsviczian
2eb5fc476c 1.9.11 2023-07-25 23:07:34 +02:00
zsviczian
bb2d30f9e3 1.9.10 2023-07-23 19:09:19 +02:00
zsviczian
71582220ee added ellipse elements script 2023-07-23 19:07:15 +02:00
zsviczian
cfc872f3f1 Merge pull request #1212 from mazurov/master
Add script for drawing ellipse around selected elements
2023-07-23 18:56:57 +02:00
zsviczian
d914bd0678 1.9.9 2023-07-22 22:18:05 +02:00
zsviczian
a5fdf6efbb deleted ExcalidrawAutomateInterface declaration 2023-07-22 14:42:25 +02:00
zsviczian
02b1f035d3 ea.newFilePrompt 2023-07-22 10:02:07 +02:00
zsviczian
395fde7982 Stop tracking yarn.lock 2023-07-22 07:55:20 +02:00
zsviczian
8edab82308 release notes, fixing changes to match excalidraw package (twitter), 1.9.9 release notes. 2023-07-22 07:39:12 +02:00
zsviczian
d483ac55b5 Tweaks for ExcaliBrain next release 2023-07-17 22:48:09 +02:00
zsviczian
982f206ca4 shouldRestoreElements 2023-07-17 13:36:42 +00:00
zsviczian
1a9f56bb09 updated ExcalidrawAutomateInterface definition and exported type library 2023-07-16 21:10:03 +02:00
zsviczian
8ff312b8e4 correct embed link for twitter (and others in the future) in the exported SVG 2023-07-16 15:36:42 +02:00
zsviczian
d92349925a imagecache fix 2023-07-16 14:54:54 +02:00
zsviczian
d8cd929ebe migrated from "iframe" to "embeddable" 2023-07-16 09:39:46 +02:00
zsviczian
58d8780ac8 slideshow with video link 2023-07-15 15:24:29 +02:00
zsviczian
d3a0e43a2b fixed frames in slideshow script 2023-07-15 13:40:51 +02:00
zsviczian
0f5744eb43 archive alternative pens 2023-07-15 13:15:31 +02:00
zsviczian
85386c6b9b delete alternative pens 2023-07-15 13:13:31 +02:00
zsviczian
f708bf14fc removed alternative pens 2023-07-15 13:12:19 +02:00
zsviczian
64388096b8 publish updated slideshow script 2023-07-15 12:36:22 +02:00
Alexander Mazurov
ee92f91b86 Add script for drawing ellipse around selected elements
Based on the "Box Selected Elements" script
2023-07-13 14:48:56 +02:00
199 changed files with 32078 additions and 17746 deletions

View File

@@ -0,0 +1,50 @@
name: Contribution Request
description: Request access to contribute to the Excalidraw plugin documentation on the wiki
title: '[Contribution] Request to Contribute to Wiki'
labels: [collaboration-request]
assignees: []
body:
- type: markdown
attributes:
value: |
## Contribution Request
Thank you for your interest in contributing to the Excalidraw Plugin wiki! To help me understand how youd like to contribute, please provide the following details:
1. **Contribution Area**: Describe what areas of the wiki youre interested in contributing to (e.g., fixing typos, adding new tutorials, improving existing content).
2. **Experience**: Briefly describe your experience with Excalidraw or Obsidian and any relevant background that will help with the contribution.
3. **Additional Information**: Any other information or questions you may have.
**Example:**
```
- Contribution Area: Adding a tutorial on advanced Excalidraw features
- Experience: Regular user of Excalidraw and experienced in creating tutorials
- Additional Information: Looking forward to contributing!
```
Once I review your request, I will get back to you with instructions on how to proceed. Thank you for helping improve our documentation!
---
Zsolt
- type: textarea
attributes:
label: Contribution Details
description: Provide details about your contribution request.
placeholder: |
- Contribution Area: Adding a tutorial on advanced Excalidraw features
- Experience: Regular user of Excalidraw and experienced in creating tutorials
- Additional Information: Looking forward to contributing!
- type: checkboxes
id: verify_guidelines
attributes:
label: Please read the WIKI Contributors Guidelines before submitting your request!
options:
- label: Yes, I have read and understood the [Contributors Guidelines](https://github.com/zsviczian/obsidian-excalidraw-plugin/wiki/Contributor-Guidelines)

68
.github/ISSUE_TEMPLATE/How-to.yml vendored Normal file
View File

@@ -0,0 +1,68 @@
name: How to? Support request
description: Ask for help with using the plugin or understanding its features.
title: "SUPPORT: "
body:
- type: markdown
attributes:
value: |
**Important: Please Read Before Submitting a Support Request**
I am a one-person team working on this plugin as a part-time hobby. Please help me manage the workload by following these guidelines. **Support requests that don't include enough details may be closed without review**.
Before submitting a support request, please:
1. **Review the [documentation](https://github.com/zsviczian/obsidian-excalidraw-plugin/wiki)** your question may already be answered.
2. **[Search issues](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues) (including closed ones)** to see if your question has already been addressed.
- type: markdown
attributes:
value: |
Please help by providing the following details. Requests without this information may be closed without review.
- type: markdown
attributes:
value: |
---
- type: checkboxes
id: search_existing_resources
attributes:
label: Have you checked the documentation and searched existing issues?
description: Please confirm that you've reviewed available resources before submitting your request.
options:
- label: Yes, I have reviewed the documentation and searched for related issues.
- type: textarea
id: support_question
attributes:
label: "Your question"
description: "Provide a clear and concise description of the question or issue you need help with."
placeholder: "Describe your question or the problem you are trying to solve..."
- type: textarea
id: steps_tried
attributes:
label: "Steps you've already tried"
description: "List any steps you've taken to try to resolve or understand the issue."
placeholder: |
1. Tried reading the documentation.
2. Searched the issues for similar questions.
- type: textarea
id: expected_outcome
attributes:
label: "Expected outcome"
description: "Describe what you expected to happen or what you are trying to achieve."
placeholder: "Describe the result you are aiming for..."
- type: textarea
id: additional_context
attributes:
label: "Additional context"
description: "Add any other context or details that may help."
placeholder: "Include any other information that may be relevant..."
- type: markdown
attributes:
value: |
**Attachments: Screenshots and files are critical!**
A picture speaks a thousand words, and a screen recording even more. **Please attach screenshots, screen recordings, or sample files** to help illustrate your question or issue. Drag and drop them into the comment area or directly into any of the text fields above.

View File

@@ -1,30 +0,0 @@
---
name: Bug report
about: Create a report to help me improve Excalidraw
title: 'BUG: '
labels: ''
assignees: ''
---
**Your environment**
Please run `Command Palette/Show Debug info` in Obsidian and paste the result here.
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

40
.github/ISSUE_TEMPLATE/bug_report.md.x vendored Normal file
View File

@@ -0,0 +1,40 @@
---
name: Bug report
about: When something is clearly broken. Everything else is a feature request.
title: 'BUG: '
labels: ''
assignees: ''
---
Help me help you. I am a one man show doing this plugin as a part time hobby. There is no point in flooding me with issues, if there are too many, and they are poorly documented, I will just ignore them. Sorry...
Before creating a bug report, please
1. review recent release notes - maybe there is already an answer,
2. search issues (including closed ones) to see if there is anything similar.
⚠️ I will have to close all recorded bugs that do not provide this background information. Sorry, I need to control my workload/time. ⚠️
--------
**Your environment**
Please run `Command Palette/Show Debug info` in Obsidian and paste the result here.
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

85
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,85 @@
name: Bug report
description: If something is clearly broken, its a bug. Everything else is a feature or support request. Most reported “bugs” are actually how-to questions or feature requests.
title: "BUG: "
body:
- type: markdown
attributes:
value: |
⚠️ **Important: Please Read Before Submitting a Bug Report** ⚠️
I am a one-person team working on this plugin as a part-time hobby. I cannot handle a flood of poorly documented issues. **To ensure your report is considered, you must follow these guidelines**. If you don't, I will close the issue without review.
Before creating a bug report, please:
1. **Review recent [release notes](https://github.com/zsviczian/obsidian-excalidraw-plugin/releases)** maybe there is already an answer.
2. **[Search issues](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues) (including closed ones)** to see if there is anything similar.
- type: markdown
attributes:
value: |
Please help by providing the following details. Bugs reported without the required information may be closed without review.
- type: markdown
attributes:
value: |
---
- type: checkboxes
id: search_existing_issues
attributes:
label: Have you searched for existing issues (including closed ones)?
description: Please confirm that you have searched the issue tracker before reporting a new issue.
options:
- label: Yes, I have searched the existing issues.
- type: checkboxes
id: verify_bug
attributes:
label: Does this bug persist in a new vault with only Excalidraw installed?
description: Please confirm that you have tested this issue in an empty Obsidian vault with no other plugins or themes installed.
options:
- label: Yes, I have verified the issue persists.
- type: textarea
id: environment
attributes:
label: "Your environment"
description: "Run `Command Palette/Show Debug info` in Obsidian and paste the result here."
placeholder: "Paste your Obsidian debug info here..."
- type: textarea
id: bug_description
attributes:
label: "Describe the bug"
description: "A clear and concise description of what the bug is."
placeholder: "Provide a detailed description of the issue..."
- type: textarea
id: steps_to_reproduce
attributes:
label: "Steps to reproduce"
description: "List the steps to reproduce the behavior."
placeholder: |
1. Go to '...'
2. Click on '...'
3. See error
- type: textarea
id: expected_behavior
attributes:
label: "Expected behavior"
description: "A clear and concise description of what you expected to happen."
placeholder: "Describe what you expected to happen..."
- type: textarea
id: additional_context
attributes:
label: "Additional context"
description: "Add any other context about the problem here."
placeholder: "Include any other information that may be helpful..."
- type: markdown
attributes:
value: |
**Attachments:**
If applicable, please attach any screenshots, screen recordings, or files by dragging and dropping them into the comment area or directly into any of the text fields above.

View File

@@ -0,0 +1,69 @@
name: Feature request
description: Request a new feature for the Excalidraw plugin.
title: "FR: "
body:
- type: markdown
attributes:
value: |
🛠️ **Important: Please Read Before Submitting a Feature Request** 🛠️
I develop Excalidraw primarily as a hobby, and I prioritize features that I personally use regularly. Features I don't use often break without me noticing, which is why the plugin is as stable as it is—because I rely on it every day.
**Your task isn't just to describe the feature you want, but to sell me on the idea**. If I'm not convinced that the feature would significantly benefit my own workflow, it's unlikely I'll spend my free hobby time on it.
When creating your feature request:
- **Provide real-life usage scenarios**: How will this feature help you? How do you imagine using it in practice?
- **Sell the idea**: Convince me that I need this feature too.
- **Include supporting materials**: Reference other solutions? Include relevant screenshots, screen recordings, and links. The more work I need to do to understand your request, the less likely it is to be implemented.
**Additional Guidelines:**
- **Excalidraw Core Features**: If your request relates to core Excalidraw features, and not the Obsidian integration specifically, please raise it with the Excalidraw product team here: [Excalidraw Issues](https://github.com/excalidraw/excalidraw/issues).
- **Cross-Platform Compatibility**: Obsidian and Excalidraw are cross-platform solutions. I cannot develop platform-specific features like Apple Pencil or Samsung S-Pen support due to technical limitations. Please only request features that make sense across all platforms (Windows, Linux, ChromeOS, macOS, Android, iOS).
Remember: You're not asking from a genie. You're pitching to someone who will need to spend hours (even for small changes) on testing, releasing, documenting, and supporting the feature. If you don't sell it well, it won't get done.
**Explore Scripting First:**
Many feature requests can already be achieved with scripts using Excalidraw Automate. While the documentation is somewhat dated, there are nearly a hundred published scripts available. With a little determination, you can explore the possibilities. Feeding relevant scripts into GPT can often generate a working solution. Start here: [Excalidraw Automate Documentation](https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html).
- type: textarea
id: problem_description
attributes:
label: "Is your feature request related to a problem? Please describe."
description: "Provide a clear and concise description of the problem. Ex. I'm always frustrated when..."
placeholder: "Describe the problem you are facing..."
- type: textarea
id: solution_description
attributes:
label: "Describe the solution you'd like"
description: "Provide a clear and concise description of what you want to happen."
placeholder: "Describe the feature you are requesting..."
- type: textarea
id: usage_scenario
attributes:
label: "Real-life usage scenarios"
description: "Provide specific examples of how you would use this feature. Convince me that this is a feature I need in my own workflow."
placeholder: |
1. In this scenario, I would use the feature to...
2. Another use case is when I...
- type: textarea
id: alternatives_considered
attributes:
label: "Describe alternatives you've considered"
description: "Provide a clear and concise description of any alternative solutions or features you've considered."
placeholder: "Describe any alternative approaches you have considered..."
- type: textarea
id: additional_context
attributes:
label: "Additional context"
description: "Include any other context, screenshots, or references about the feature request."
placeholder: "Include screenshots, recordings, or other supporting material..."
- type: markdown
attributes:
value: |
**Attachments: Make your case visually!**
Supporting images, screenshots, or screen recordings are critical for helping me understand your request. Drag and drop them into the comment area or directly into any of the text fields above. Every bit of detail increases the chance of your request being understood and prioritized.

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@ stats.html
hot-reload.bat
data.json
lib
dist
#VSCode
.vscode

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
18

View File

@@ -195,7 +195,7 @@ 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.
This is relevant when using the `addArrow()` and the `connectObjects()` functions.
## canvas
Sets the properties of the canvas.

View File

@@ -1,32 +1,4 @@
The project runs with `node 16.10.0`. Some packages will throw dependency errors if you try to compile with a higher node version.
The project runs with `node 18`.
After running `npm -i` you'll need to make two manual changes:
## postprocess
postprocess is used in rollup.config.js.
However, the version available on npmjs does not work, after installing packages you need this update:
`npm install brettz9/rollup-plugin-postprocess#update --save-dev``
More info here: https://github.com/developit/rollup-plugin-postprocess/issues/10
## colormaster
1.2.1 misses 3 plugin references after installing the package you need to update
`node_modules/colormaster/package.json` adding the following to the `exports:` section:
```typescript
,
"./plugins/luv": {
"import": "./plugins/luv.mjs",
"require": "./plugins/luv.js",
"default": "./plugins/luv.mjs"
},
"./plugins/uvw": {
"import": "./plugins/uvw.mjs",
"require": "./plugins/uvw.js",
"default": "./plugins/uvw.mjs"
},
"./plugins/ryb": {
"import": "./plugins/ryb.mjs",
"require": "./plugins/ryb.js",
"default": "./plugins/ryb.mjs"
}
```

287
README.md
View File

@@ -1,10 +1,19 @@
# Excalidraw
[简体中文](./docs/zh-cn/README.md)
👉👉👉 Check out and contribute to the new [Obsidian-Excalidraw Community Wiki](https://excalidraw-obsidian.online/WIKI/Welcome+to+the+WIKI)
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.
## Video Walkthrough
<a href="https://youtu.be/P_Q6avJGoWI" target="_blank"><img src="https://github.com/zsviczian/obsidian-excalidraw-plugin/assets/14358394/da34bb33-7610-45e6-b36f-cb7a02a9141b" width="300"/></a>
<a href="https://youtu.be/o0exK-xFP3k" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931370-aa4d88de-c4a8-46cc-aeb2-dc09aa0bea39.jpg" width="300"/></a>
<a href="https://youtu.be/QKnQgSjJVuc" target="_blank"><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/thumbnail-getting-started.jpg" width="300"/></a>
<a href="https://youtu.be/o0exK-xFP3k" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931370-aa4d88de-c4a8-46cc-aeb2-dc09aa0bea39.jpg" width="300"/></a>
### Here's my complete catalog of videos:
<a href="https://excalidraw-obsidian.online/Hobbies/Excalidraw+Blog/Catalogue+of+Videos"><img width="380" alt="image" src="https://github.com/zsviczian/obsidian-excalidraw-plugin/assets/14358394/2577e5ad-7a21-4c62-acd5-4fe80c8a8a95"></a>
<br>
<details><summary>10 Part (slightly outdated) Video Walkthrough</summary>
<a href="https://youtu.be/sY4FoflGaiM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160304-7f211180-e17c-11eb-8363-c52723de1ffd.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;1 Getting Started</a><br>
@@ -58,55 +67,59 @@ The Obsidian-Excalidraw plugin integrates [Excalidraw](https://excalidraw.com/),
<a href="https://youtu.be/4N6efq1DtH0" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/158008902-12c6a851-237e-4edd-a631-d48e81c904b2.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Eraser, left-handed mode, improved filename configuration</a><br>
</details>
### Beta testing
The plugin follows a monthly release schedule. If you want to receive more frequent updates with new features (e.g. shiny new stuff available on excalidraw.com, but not yet in Obsidian) and minor bug fixes, then join the beta community.
[![Thumbnail - 20240803 Excalidraw Release Approach (Custom)](https://github.com/user-attachments/assets/ab40648c-f73f-4bda-a416-52839f918f2a)](https://youtu.be/2poSS-Z91lY)
[![Excalidraw Plugin Release Strategy (Phone)](https://github.com/user-attachments/assets/87f1f379-782c-4c32-8b5b-d27fe2d3ac4b)](https://github.com/user-attachments/assets/120a0790-7239-48ae-bfbd-eb249f8b518d)
---
## Features
- The plugin integrates Excalidraw seamlessly into Obsidian including Command Palette actions, File
Explorer features, Option Menu commands, and the Ribbon Button.
- <kbd>CTRL/CMD+Click</kbd> on the ribbon button, or in the file explorer to create / open drawings
in a new pane.
- The plugin integrates Excalidraw seamlessly into Obsidian, including Command Palette actions, File Explorer features, Option Menu commands, and the Ribbon Button.
- <kbd>CTRL/CMD+Click</kbd> on the ribbon button or in the file explorer to create / open drawings in a new pane.
### Settings
Settings will allow you to customize Excalidraw to your needs. The plugin comes with tons of settings. I tried adding meaningful explanations to these settings, so please be patient and look for the setting, for most requests a setting already exists.
Settings will allow you to customize Excalidraw to your needs. The plugin comes with tons of settings. I tried adding meaningful explanations to these settings, so please be patient and look for the setting, for most requests, a setting already exists.
Plugin settings are grouped into the following sections:
- **Basic settings**: such as default folders to use
- **Saving**: compression and autosave timer
- **Filename**: configure the automatically created Excalidraw filename
- **Display**: settings that effect the handling of Excalidraw (e.g.: left-handed mode, theme settings, mouse wheel and pinch zoom settings, zoom to fit settings)
- **Links and transclusions**: Settings that effect how links and embedded items behave on the Excalidraw canvas
- **Markdown-embed settings**: These settings control how markdown documents from your Vault embedded into Excalidraw drawings will behave
- **Embed & Export**: Settings that control how Excalidraw images are displayed when embedding them into markdown documents
- **Auto-export Settings**: You can configure Excalidraw to create a PNG or SVG copy of your drawing each time it gets saved
- **Compatibility features**: Check these settings if you edit the Excalidraw drawings outside Obsidian (e.g. in LogSeq, Visual Studio, on the web, etc.)
- **Basic settings**: such as default folders to use.
- **Saving**: compression and autosave timer.
- **Filename**: configure the automatically created Excalidraw filename.
- **Display**: settings that effect the handling of Excalidraw (e.g.: left-handed mode, theme settings, mouse wheel and pinch zoom settings, zoom to fit settings).
- **Links and transclusions**: Settings that effect how links and embedded items behave on the Excalidraw canvas.
- **Markdown-embed settings**: These settings control how markdown documents from your Vault embedded into Excalidraw drawings will behave.
- **Embed & Export**: Settings that control how Excalidraw images are displayed when embedding them into markdown documents.
- **Auto-export Settings**: You can configure Excalidraw to create a PNG or SVG copy of your drawing each time it gets saved.
- **Compatibility features**: Check these settings if you edit the Excalidraw drawings outside Obsidian (e.g. in LogSeq, Visual Studio, on the web, etc.).
- **Experimental features**: There are advanced features that are implemented as "clever" hacks. Features include defining a fourth font, adding a custom icon to distinguish Exalidraw files in the Obsidian file explorer, OCR settings, and more.
- **Settings for installed Scripts**: Some of the scripts you install from the Script Library come with settings. Script settings are installed the first time you run the script. So to access settings for a script, install the script, run it for the first time, then look for the settings in plugin settings.
- **Settings for installed Scripts**: Some of the scripts you install from the Script Library come with settings. Script settings are installed the first time you run the script. So to access settings for a script, install the script, run it for the first time and then look for the settings in plugin settings.
#### Templates
- Template for new drawings. The template will restore stroke properties.
This means you can set up defaults in your template for stroke color, stroke width,
opacity, font family, font size, fill style, stroke style, etc.
This also applies to ExcalidrawAutomate.
- Via the template you can customize the color palette used by Excalidraw.
- Switch to Markdown view.
- Scroll down to the bottom of the file and find `"AppState": {`.
- Find `"customColorPalette": {` at the end of the AppState section.
- 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. With versions 1.6.13 or higher make sure to enable "Decompress Excalidraw JSON in Markdown View" in the settings before editing the JSON in the template. This can be disabled after the canges are performed.
- Via the template, you can customize the color palette used by Excalidraw.
- Switch to Markdown view.
- Scroll down to the bottom of the file and find `"AppState": {`.
- Find `"colorPalette": {` at the end of the AppState section.
- You may specify the 3 palettes used in Excalidraw by adding any or all of the following 3 variables:
- `"canvasBackground":[], "elementBackground":[], "elementStroke": []`.
- Add a comma separated list of valid HTML colors (e.g. `#FF0000` for red)
in the array for each of the variables.
- See my videos above for further help.
- Add a comma-separated list of valid HTML colors (e.g. `#FF0000` for red) in the array for each of the variables.
- To change the previewed colors, a `"topPicks": {` may be specified containing the same three keys:
- `"canvasBackground":[], "elementBackground":[], "elementStroke": []`.
- Note that the corresponding arrays must contain 5 elements.
- See my videos above for further help.
#### Export
- If portability is important to you:
- Auto-export SVG and/or PNG files including keep-in-sync feature so you can
embed SVG/PNG into your documents instead of embedding excalidraw files.
- You can override export settings for an individual file by adding the `excalidraw-autoexport`
frontmatter key. Valid values for this key are `none`, `both`, `png` and `svg`.
- Auto-export SVG and/or PNG files, including the keep-in-sync feature, so you can
embed SVG/PNG into your documents instead of embedding excalidraw files.
- You can override export settings for an individual file by adding the `excalidraw-autoexport`
frontmatter key. Valid values for this key are `none`, `both`, 'png', and `svg`.
- Specify the default width of embedded drawings.
- Compatibility features to auto-export and keep in sync markdown excalidraw files and legacy `.excalidraw` files.
@@ -114,137 +127,145 @@ Plugin settings are grouped into the following sections:
- Enable / disable autosave.
### Embedding your drawings into markdown documents
- You can customize the size and position of the embedded images using the
- `![[image.excalidraw|100]]`,
- `![[image.excalidraw|100x100]]`,
- `![[image.excalidraw|100|left]]`,
- `![[image.excalidraw|right-wrap]]`, formatting options.
- `![[<filename.excalidraw>|<width>x<height>|<alignment>]]`.
- You can add your custom [alignment via CSS](https://www.scaler.com/topics/align-image-in-html/).
- Any text that appears in `<alignment>` will be added to the rendered SVG element style and to the wrapper DIV element.
- See [styles.css](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/styles.css) for more insight.
- `![[image.excalidraw|100]]`,
- `![[image.excalidraw|100x100]]`,
- `![[image.excalidraw|100|left]]`,
- `![[image.excalidraw|right-wrap]]`, formatting options.
- `![[<filename.excalidraw>|<width>x<height>|<alignment>]]`.
- You can add your custom [alignment via CSS](https://www.scaler.com/topics/align-image-in-html/).
- Any text that appears in `<alignment>` will be added to the rendered SVG element style and to the wrapper DIV element.
- See [styles.css](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/styles.css) for more insight.
- Excalidraw drawings do not display in Obsidian Publish. If you want to use Excalidraw in your published documents, you can configure in plugin settings, under `Embed & Export`, to automatically insert a PNG or SVG version of the drawing in your document when creating a new file. See `type of file to insert into document`
- Under `Export settings` you can also configure to automatically export a dark and light version of the image, in case your published site supports dark and light mode.
- Under `Export settings`, you can also configure to automatically export a dark and light version of the image, in case your published site supports dark and light modes.
### Hyperlinks and Drag & Drop support
![](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/images/excalidraw-modifiers.png)
#### Hyperlinks
- Supports hyperlinks e.g.
- `https://zsolt.blog`,
- `[Obsidian](https://obsidian.md)`, and
- internal links e.g. `[[My file in vault|Alias]]` in drawing text.
- Links will update when files are moved or renamed, if you have the Obsidian
setting Files & Links/Automatically Update Internal Links enabled.
- Links in drawings will show up in backlinks of documents
- Transclusions are supported
- `![[myfile#^blockref]]` will convert in the drawing into the transcluded text of the block
- `![[myfile#section]]` also works, this will transclude the section
- you can also specify word wrapping for transcluded text by adding the max character count
in curly brackets right after the transclusion e.g. `![[myfile#^blockref]]{40}` will wrap text at 40 characters.
- For convenience you can also use the command palette to insert links into drawings
- <kbd>CTRL/CMD + hover</kbd> to bring up the Obsidian quick preview for the link. (On Mac it is <kbd>CTRL+CMD+hover</kbd>).
- Using the block reference you can also reference & transclude text that appears on drawings, in other documents
- 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 the backlinks of documents.
- Transclusions are supported:
- `![[myfile#^blockref]]` will convert the drawing into the transcluded text of the block
- `![[myfile#section]]` also works, this will transclude the section.
- You can also specify word wrapping for transcluded text by adding the max character count:
in curly brackets right after the transclusion e.g. `![[myfile#^blockref]]{40}` will wrap text at 40 characters.
- For convenience, you can also use the command palette to insert links into drawings.
- <kbd>CTRL/CMD + hover</kbd> to bring up the Obsidian quick preview for the link. (On Mac, it is <kbd>CTRL+CMD+hover</kbd>).
- Using the block reference, you can also reference & transclude text that appears on drawings, in other documents.
#### Drag & Drop support
- You can drag files from the Obsidian file explorer and they will become links to those files in Excalidraw. See table above for the varios modifier key combinations.
- Note: anchoring an image to 100% of its size is a very niche feature with a very particular behavior that I built primarily for myself
- (even more so than other features in Excalidraw Obsidian - also built primarily for myself 😉).
- This will reset your embedded image to 100% size every time you open the Excalidraw drawing,
or in case you have embedded an Excalidraw drawing on your canvas inserted using this function,
every time you update the embedded drawing, it will be scaled back to 100% size.
- This means that even if you resize the image on the drawing, it will reset to 100% the next time you open
the file or you modify the original embedded object. This feature is useful when you
decompose a drawing into separate Excalidraw files, but when combined onto a single canvas
you want the individual pieces to maintain their actual sizes. I use this feature to
construct Book-on-a-Page summaries from atomic drawings.
- You can drag files from the Obsidian file explorer, and they will become links to those files in Excalidraw. See the table above for the various modifier key combinations.
- Note: Anchoring an image to 100% of its size is a very niche feature with a very particular behavior that I built primarily for myself.
- (even more so than other features in Excalidraw Obsidian - also built primarily for myself 😉).
- This will reset your embedded image to 100% size every time you open the Excalidraw drawing,
or in case you have embedded an Excalidraw drawing on your canvas inserted using this function,
every time you update the embedded drawing, it will be scaled back to 100% size.
- This means that even if you resize the image on the drawing, it will reset to 100% the next time you open
the file, or you modify the original embedded object. This feature is useful when you
decompose a drawing into separate Excalidraw files, but when combined onto a single canvas
you want the individual pieces to maintain their actual sizes. I use this feature to
construct book-on-a-page summaries from atomic drawings.
- You can drag and drop text from Markdown views onto Excalidraw.
- You can drag and drop web addresses from your browser and they will become links.
- You can drag and drop YouTube links and thumbnails and they will be YouTube links with thumbnails in Excalidraw
- You can drag and drop web addresses from your browser, and they will become links.
- You can drag and drop YouTube links and thumbnails, and they will be YouTube links with thumbnails in Excalidraw.
### LaTeX
Insert LaTeX formulas using the Command Palette action "Insert LaTeX formula".
You can edit formulas either in Markdown view, or by <kbd>CTRL/CMD + Click</kbd> on the formula.
You can edit formulas either in Markdown view or by <kbd>CTRL/CMD + Click</kbd> on the formula.
### Image support
- On iOS and Android you can add images from your camera by pressing the add image button in Excalidraw.
- 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.
- URL link to images on the web: You can drag images from a webpage to Excalidraw. If you hold down the CTRL button while dropping the image to Excalidraw, the image will not be saved to your vault. Excalidraw will load the image from the URL. Note, that if you do not have internet access, or these images are deleted from the internet, they will also disappear from your drawing.
- If you page an image URL to excalidraw (simply click copy on the url, then click paste on the excalidraw canvas), the image will be inserted with a link to the image on the web. Again, the image won't be save to your vault, only the link.
- If you drop a YouTube video link it will be convereted into a thumbnail photo with an element link pointing to the video.
- URL link to images on the web: You can drag images from a webpage to Excalidraw. If you hold down the CTRL button while dropping the image to Excalidraw, the image will not be saved to your vault. Excalidraw will load the image from the URL. Note that if you do not have internet access or if these images are deleted from the internet, they will also disappear from your drawing.
- If you page an image URL to excalidraw (simply click copy on the url, then click paste on the excalidraw canvas), the image will be inserted with a link to the image on the web. Again, the image won't be saved to your vault, only the link.
- If you drop a YouTube video link, it will be converted into a thumbnail photo with an element link pointing to the video.
### Block referencing parts of images
For more details see this [video](https://youtu.be/yZQoJg2RCKI)
For more details, see this [video](https://youtu.be/yZQoJg2RCKI)
- When referencing an element on the canvas in a link pointing to an Excalidraw file using
- the elementId or the section header (i.e. a Text Element containing the `# <Section title>`)
- e.g. `[[file#^elementID]]`,
- you can add the `group=` prefix,
- e.g. `[[file#^group=elementID]]` or
- the `area=` prefix,
- e.g. `[[file#area=Section heading]]`.
- If the `group=` prefix is found Excalidraw will select the group of elements in the
same group as the element referenced by the elementID (block reference) or the section heading.
- If the `area=` prefix is found Excalidraw will insert a cutout of the image around the referenced element.
- Note that the `area=` selector is not supported when embedding Excalidraw as PNG into your markdown documents.
- Referencing the elementID of a text element without the `group=` or `area=` prefix will
transclude the element as plain text. Referencing a non-Text Element (e.g. rectangle,
ellipse, etc.) without the `group=` or `area=` prefix will result in an Obsidian error
since these elementIds are not present in the Excalidraw markdown file as block
references.
- The elementId or the section header (i.e. a Text Element containing the `# <Section title>`)
- e.g. `[[file#^elementID]]`,
- You can add the `group=` prefix,
- e.g. `[[file#^group=elementID]]` or
- The `area=` prefix,
- e.g. `[[file#area=Section heading]]`.
- If the `group=` prefix is found, Excalidraw will select the group of elements in the
same group as the element referenced by the elementID (block reference) or the section heading.
- If the `area=` prefix is found, Excalidraw will insert a cutout of the image around the referenced element.
- Note that the `area=` selector is not supported when embedding Excalidraw as a PNG into your markdown documents.
- Referencing the elementID of a text element without the `group=` or `area=` prefix will
transclude the element as plain text. Referencing a non-Text Element (e.g. rectangle,
ellipse, etc.) without the `group=` or `area=` prefix will result in an Obsidian error.
since these elementIds are not present in the Excalidraw markdown file as block
references.
### Markdown
- Since 1.2.0 Drawing files are stored in Markdown files
- You can add tags to drawings
- You can add metadata to the YAML front matter of drawings
- Anything you add between the frontmatter and the `# Text Elements` heading will be ignored by Excalidraw, i.e. you can add whatever you like here, it will be preserved as part of the document.
- Excalidraw documents now show in graph view.
- The following front matter keys will customize how the drawing is displayed - overriding general settings:
- `excalidraw-link-prefix: "📍"` preview prefix for internal links
- `excalidraw-url-prefix: "🌐"` preview prefix for external links
- `excalidraw-link-brackets: true|false` whether or not to display brackets around links in preview
- `excalidraw-default-mode: view|zen` Open this document in view mode or zen mode by defult. Default view mode is excellent for presentation slides.
- Frontmatter tags to customize image export at a file level [519](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/519). If these keys are present they will override the default excalidraw embed and export settings.
- `excalidraw-export-transparent: true`: true == Transparent / false == with background.
- `excalidraw-export-dark`: true == Dark mode / false == light mode.
- `excalidraw-export-padding`: Specify the export padding for the image
- `excalidraw-export-pngscale`: This only affects export to PNG. Specify the export scale for the image. The typical range is between 0.5 and 5, but you can experiment with other values as well.
- 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, and it will be preserved as part of the document.
- Excalidraw documents now show up in graph view.
- The following front matter keys will customize how the drawing is displayed - overriding general settings:
- `excalidraw-link-prefix: "📍"` preview prefix for internal links
- `excalidraw-url-prefix: "🌐"` preview prefix for external links
- `excalidraw-link-brackets: true|false` whether or not to display brackets around links in preview
- `excalidraw-default-mode: view|zen` Open this document in view mode or zen mode by defult. The default view mode is excellent for presentation slides.
- Frontmatter tags to customize image export at a file level [519](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/519). If these keys are present, they will override the default Excalidraw embed and export settings.
- `excalidraw-export-transparent: true`:  true == Transparent / false == with background.
- `excalidraw-export-dark`: true == Dark mode / false == light mode.
- `excalidraw-export-padding`: Specify the export padding for the image.
- `excalidraw-export-pngscale`: This only affects export to PNG. Specify the export scale for the image. The typical range is between 0.5 and 5, but you can experiment with other values as well.
- Since 1.6.13, enable "Decompress Excalidraw JSON in Markdown View" in the settings if you want to change any JSON content.
### Embed complete markdown files into your drawings
- Drag from the desired file from the Obsidian file explorer and hold down <kbd>SHIFT</kbd> while dropping the file onto the canvas.
Drag the desired file from the Obsidian file explorer and hold down <kbd>SHIFT</kbd> while dropping the file onto the canvas.
- Use the command palette action: `Insert markdown file from vault`
- Use custom woff, woff2 or TTF font to display the document, you can set the default font to use under Excalidraw Settings.
- You can set a custom css for rendering the snapshot image of your markdown document.
Only operating system standard fonts are supported as the font-family (
[Win10](https://docs.microsoft.com/en-us/typography/fonts/windows_10_font_list),
[Mac & iOS](https://developer.apple.com/fonts/system-fonts/)
), plus you can set one additional custom font using the setting explained above.
- (for a demonstration watch this [video](https://youtu.be/K6qZkTz8GHs) and check out this
- [sample css](https://github.com/zsviczian/obsidian-excalidraw-plugin/discussions/281)).
- To help with styling you can observe the SVG snapshot of the markdown document created by Excalidraw.
- Open Obsidian Developer Console (<kbd>CTRL+Shift+i</kbd>/<kbd>CMD+OPT+i</kbd>) and
- execute the following command: `ExcalidrawAutomate.mostRecentMarkdownSVG`
- You can control appearance of the embedded markdown file on a file by file
bases by adding the following front matter keys to your markdown document:
- `excalidraw-font: Virgil|Cascadia|font_file_name.extension`
- `excalidraw-font-color: css-color-name|#HEXcolor|any-other-html-standard-format`,
- you can find css color names [here](https://www.w3schools.com/colors/colors_names.asp).
- `excalidraw-border-color: css-color-name|#HEXcolor|any-other-html-standard-format`
- `excalidraw-css: "css-filename|css snippet"`
- Switch to markdown view or use <kbd>WIN+CTRL</kbd>/<kbd>CMD+CTRL</kbd> click on the image to edit properties of the embed:
- `[[filename#^blockref|WIDTHxMAXHEIGHT]]`
- Use custom woff, woff2, or TTF font to display the document, you can set the default font to use under Excalidraw Settings.
- You can set a custom CSS for rendering the snapshot image of your markdown document. Only operating system-standard fonts are supported as the font-family ([Win10](https://docs.microsoft.com/en-us/typography/fonts/windows_10_font_list), [Mac & iOS](https://developer.apple.com/fonts/system-fonts/)), plus you can set one additional custom font using the setting explained above.
- (for a demonstration, watch this [video](https://youtu.be/K6qZkTz8GHs) and check out this
- [sample css](https://github.com/zsviczian/obsidian-excalidraw-plugin/discussions/281)).
- To help with styling, you can observe the SVG snapshot of the markdown document created by Excalidraw.
- Open Obsidian Developer Console (<kbd>CTRL+Shift+i</kbd>/<kbd>CMD+OPT+i</kbd>) and
- Execute the following command: `ExcalidrawAutomate.mostRecentMarkdownSVG`
- You can control the appearance of the embedded markdown file on a file by file
  bases by adding the following front matter keys to your markdown document:
- `excalidraw-font: Virgil|Cascadia|font_file_name.extension`
- `excalidraw-font-color: css-color-name|#HEXcolor|any-other-html-standard-format`,
- You can find css color names [here](https://www.w3schools.com/colors/colors_names.asp).
- `excalidraw-border-color: css-color-name|#HEXcolor|any-other-html-standard-format`
- `excalidraw-css: "css-filename|css snippet"`
- Switch to markdown view or use <kbd>WIN+CTRL</kbd>/<kbd>CMD+CTRL</kbd> click on the image to edit the properties of the embed:
- `[[filename#^blockref|WIDTHxMAXHEIGHT]]`
### Custom Font, Custom Pen, OCR support, SVG import
- In plugin settings you can add a custom 4th font. For more details see this [video](https://youtu.be/eKFmrSQhFA4)
- The plugin includes OCR support using Taskbone OCR. For more details see this [video](https://youtu.be/7gu4ETx7zro)
- You can convert SVG files into Excalidraw drawings (with some limitation). For more details see this [video](https://youtu.be/vlC1-iBvIfo)
- You can define custom freedraw pens. See documentation [here](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Alternative%20Pens.md), [video](https://youtu.be/uZz5MgzWXiM)
- In plugin settings, you can add a custom fourth font. For more details, see this [video](https://youtu.be/eKFmrSQhFA4)
- The plugin includes OCR support using Taskbone OCR. For more details, see this [video](https://youtu.be/7gu4ETx7zro)
- You can convert SVG files into Excalidraw drawings (with some limitation). For more details, see this [video](https://youtu.be/vlC1-iBvIfo)
- You can define custom pens and higlighters and pin them to the sidebar. For more details, see this [video](https://youtu.be/OjNhjaH2KjI). Using ExcalidrawAutomate, you can add support for [auto-toggling](<ea-scripts/Auto Draw for Pen.md>) pen & support for [hardware eraser buttons](<ea-scripts/Hardware Eraser Support.md>).
### Script Engine
- 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).
- You can organize scripts into groups on the Obsidian Tools Panel in Excalidraw by moving scripts and accompanying SVG icon files to folders. See demo [video](https://youtu.be/wTtaXmRJ7wg?t=16).
- 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](ea-scripts/README.md).
- You can organize scripts into groups on the Obsidian Tools Panel in Excalidraw by moving scripts and accompanying SVG icon files to folders. See the demo [video](https://youtu.be/wTtaXmRJ7wg?t=16).
### Other
- Left-handed mode
- Includes full
- [QuickAdd](https://github.com/chhoumann/quickadd),
@@ -269,7 +290,7 @@ report a bug or request an enhancement.
## Say Thank You
If you are enjoying Excalidraw then please support my work and enthusiasm by buying me a coffee on
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,
@@ -284,4 +305,4 @@ You can find me on Twitter [@zsviczian](https://twitter.com/zsviczian), and on m
## Friends of Excalidraw
If you enjoy Excalidraw, consider giving [ExcaliBrain](https://github.com/zsviczian/excalibrain) a try (also available via Obsidian Community Plugins).
<a href="https://youtu.be/gOkniMkDPyM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/169708346-9e41289d-9536-43ec-8f70-2d2ad2d369d6.png" width="300"/></a>
<a href="https://youtu.be/gOkniMkDPyM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/169708346-9e41289d-9536-43ec-8f70-2d2ad2d369d6.png" width="300"/></a>

BIN
assets/excalidraw-fonts.zip Normal file

Binary file not shown.

View File

@@ -1,36 +0,0 @@
import fs from'fs';
import LZString from 'lz-string';
const excalidraw_pkg = isProd
? fs.readFileSync("./node_modules/@zsviczian/excalidraw/dist/excalidraw.production.min.js", "utf8")
: fs.readFileSync("./node_modules/@zsviczian/excalidraw/dist/excalidraw.development.js", "utf8");
const react_pkg = isProd
? fs.readFileSync("./node_modules/react/umd/react.production.min.js", "utf8")
: fs.readFileSync("./node_modules/react/umd/react.development.js", "utf8");
const reactdom_pkg = isProd
? fs.readFileSync("./node_modules/react-dom/umd/react-dom.production.min.js", "utf8")
: fs.readFileSync("./node_modules/react-dom/umd/react-dom.development.js", "utf8");
const lzstring_pkg = fs.readFileSync("./node_modules/lz-string/libs/lz-string.min.js", "utf8")
const packageString = lzstring_pkg+'const EXCALIDRAW_PACKAGES = "' + LZString.compressToBase64(react_pkg + reactdom_pkg + excalidraw_pkg) +'";var ExcalidrawPackageLoader=(d=document)=>{const excalidraw_id = "excalidraw-script";if(!d.getElementById(excalidraw_id)){const script=d.createElement("script");script.type="text/javascript";script.id=excalidraw_id;script.text=LZString.decompressFromBase64(EXCALIDRAW_PACKAGES);d.body.appendChild(script);}};ExcalidrawPackageLoader();';
const mainjs = fs.readFileSync("main.js", "utf8")
fs.writeFileSync(
"main2.js",
mainjs
.replace('(require("react"));','')
.replace('"use strict";','"use strict";' + packageString),
{
encoding: "utf8",
flag: "w",
mode: 0o666
}
);
let config = {
};
export default config;

773
docs/API/ExcalidrawAutomate.d.ts vendored Normal file
View File

@@ -0,0 +1,773 @@
/// <reference types="react" />
import ExcalidrawPlugin from "src/main";
import { FillStyle, StrokeStyle, ExcalidrawElement, ExcalidrawBindableElement, FileId, NonDeletedExcalidrawElement, ExcalidrawImageElement, StrokeRoundness, RoundnessType } from "@zsviczian/excalidraw/types/excalidraw/element/types";
import { Editor, OpenViewState, TFile, WorkspaceLeaf } from "obsidian";
import * as obsidian_module from "obsidian";
import ExcalidrawView, { ExportSettings } from "src/ExcalidrawView";
import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI, Point } from "@zsviczian/excalidraw/types/excalidraw/types";
import { EmbeddedFilesLoader } from "src/EmbeddedFileLoader";
import { ConnectionPoint, DeviceType } from "src/types/types";
import { ColorMaster } from "colormaster";
import { TInput } from "colormaster/types";
import { ClipboardData } from "@zsviczian/excalidraw/types/excalidraw/clipboard";
import { PaneTarget } from "src/utils/ModifierkeyHelper";
export declare class ExcalidrawAutomate {
/**
* Utility function that returns the Obsidian Module object.
*/
get obsidian(): typeof obsidian_module;
get DEVICE(): DeviceType;
getAttachmentFilepath(filename: string): Promise<string>;
/**
* Prompts the user with a dialog to select new file action.
* - create markdown file
* - create excalidraw file
* - cancel action
* The new file will be relative to this.targetView.file.path, unless parentFile is provided.
* If shouldOpenNewFile is true, the new file will be opened in a workspace leaf.
* targetPane control which leaf will be used for the new file.
* Returns the TFile for the new file or null if the user cancelled the action.
* @param newFileNameOrPath
* @param shouldOpenNewFile
* @param targetPane //type PaneTarget = "active-pane"|"new-pane"|"popout-window"|"new-tab"|"md-properties";
* @param parentFile
* @returns
*/
newFilePrompt(newFileNameOrPath: string, shouldOpenNewFile: boolean, targetPane?: PaneTarget, parentFile?: TFile): Promise<TFile | null>;
/**
* Generates a new Obsidian Leaf following Excalidraw plugin settings such as open in Main Workspace or not, open in adjacent pane if available, etc.
* @param origo // the currently active leaf, the origin of the new leaf
* @param targetPane //type PaneTarget = "active-pane"|"new-pane"|"popout-window"|"new-tab"|"md-properties";
* @returns
*/
getLeaf(origo: WorkspaceLeaf, targetPane?: PaneTarget): WorkspaceLeaf;
/**
* Returns the editor or leaf.view of the currently active embedded obsidian file.
* If view is not provided, ea.targetView is used.
* If the embedded file is a markdown document the function will return
* {file:TFile, editor:Editor} otherwise it will return {view:any}. You can check view type with view.getViewType();
* @param view
* @returns
*/
getActiveEmbeddableViewOrEditor(view?: ExcalidrawView): {
view: any;
} | {
file: TFile;
editor: Editor;
} | null;
plugin: ExcalidrawPlugin;
elementsDict: {
[key: string]: any;
};
imagesDict: {
[key: FileId]: any;
};
mostRecentMarkdownSVG: SVGSVGElement;
style: {
strokeColor: string;
backgroundColor: string;
angle: number;
fillStyle: FillStyle;
strokeWidth: number;
strokeStyle: StrokeStyle;
roughness: number;
opacity: number;
strokeSharpness?: StrokeRoundness;
roundness: null | {
type: RoundnessType;
value?: number;
};
fontFamily: number;
fontSize: number;
textAlign: string;
verticalAlign: string;
startArrowHead: string;
endArrowHead: string;
};
canvas: {
theme: string;
viewBackgroundColor: string;
gridSize: number;
};
colorPalette: {};
constructor(plugin: ExcalidrawPlugin, view?: ExcalidrawView);
/**
*
* @returns the last recorded pointer position on the Excalidraw canvas
*/
getViewLastPointerPosition(): {
x: number;
y: number;
};
/**
*
* @returns
*/
getAPI(view?: ExcalidrawView): ExcalidrawAutomate;
/**
* @param val //0:"hachure", 1:"cross-hatch" 2:"solid"
* @returns
*/
setFillStyle(val: number): "hachure" | "cross-hatch" | "solid";
/**
* @param val //0:"solid", 1:"dashed", 2:"dotted"
* @returns
*/
setStrokeStyle(val: number): "solid" | "dashed" | "dotted";
/**
* @param val //0:"round", 1:"sharp"
* @returns
*/
setStrokeSharpness(val: number): "round" | "sharp";
/**
* @param val //1: Virgil, 2:Helvetica, 3:Cascadia
* @returns
*/
setFontFamily(val: number): "Virgil, Segoe UI Emoji" | "Helvetica, Segoe UI Emoji" | "Cascadia, Segoe UI Emoji" | "Local Font";
/**
* @param val //0:"light", 1:"dark"
* @returns
*/
setTheme(val: number): "light" | "dark";
/**
* @param objectIds
* @returns
*/
addToGroup(objectIds: string[]): string;
/**
* @param templatePath
*/
toClipboard(templatePath?: string): Promise<void>;
/**
* @param file: TFile
* @returns ExcalidrawScene
*/
getSceneFromFile(file: TFile): Promise<{
elements: ExcalidrawElement[];
appState: AppState;
}>;
/**
* get all elements from ExcalidrawAutomate elementsDict
* @returns elements from elementsDict
*/
getElements(): ExcalidrawElement[];
/**
* get single element from ExcalidrawAutomate elementsDict
* @param id
* @returns
*/
getElement(id: string): ExcalidrawElement;
/**
* create a drawing and save it to filename
* @param params
* filename: if null, default filename as defined in Excalidraw settings
* foldername: if null, default folder as defined in Excalidraw settings
* @returns
*/
create(params?: {
filename?: string;
foldername?: string;
templatePath?: string;
onNewPane?: boolean;
frontmatterKeys?: {
"excalidraw-plugin"?: "raw" | "parsed";
"excalidraw-link-prefix"?: string;
"excalidraw-link-brackets"?: boolean;
"excalidraw-url-prefix"?: string;
"excalidraw-export-transparent"?: boolean;
"excalidraw-export-dark"?: boolean;
"excalidraw-export-padding"?: number;
"excalidraw-export-pngscale"?: number;
"excalidraw-default-mode"?: "view" | "zen";
"excalidraw-onload-script"?: string;
"excalidraw-linkbutton-opacity"?: number;
"excalidraw-autoexport"?: boolean;
};
plaintext?: string;
}): Promise<string>;
/**
*
* @param templatePath
* @param embedFont
* @param exportSettings use ExcalidrawAutomate.getExportSettings(boolean,boolean)
* @param loader use ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?)
* @param theme
* @returns
*/
createSVG(templatePath?: string, embedFont?: boolean, exportSettings?: ExportSettings, loader?: EmbeddedFilesLoader, theme?: string, padding?: number): Promise<SVGSVGElement>;
/**
*
* @param templatePath
* @param scale
* @param exportSettings use ExcalidrawAutomate.getExportSettings(boolean,boolean)
* @param loader use ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?)
* @param theme
* @returns
*/
createPNG(templatePath?: string, scale?: number, exportSettings?: ExportSettings, loader?: EmbeddedFilesLoader, theme?: string, padding?: number): Promise<any>;
/**
*
* @param text
* @param lineLen
* @returns
*/
wrapText(text: string, lineLen: number): string;
private boxedElement;
addIFrame(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string;
/**
*
* @param topX
* @param topY
* @param width
* @param height
* @returns
*/
addEmbeddable(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string;
/**
*
* @param topX
* @param topY
* @param width
* @param height
* @returns
*/
addRect(topX: number, topY: number, width: number, height: number): string;
/**
*
* @param topX
* @param topY
* @param width
* @param height
* @returns
*/
addDiamond(topX: number, topY: number, width: number, height: number): string;
/**
*
* @param topX
* @param topY
* @param width
* @param height
* @returns
*/
addEllipse(topX: number, topY: number, width: number, height: number): string;
/**
*
* @param topX
* @param topY
* @param width
* @param height
* @returns
*/
addBlob(topX: number, topY: number, width: number, height: number): string;
/**
* Refresh the size of a text element to fit its contents
* @param id - the id of the text element
*/
refreshTextElementSize(id: string): void;
/**
*
* @param topX
* @param topY
* @param text
* @param formatting
* box: if !null, text will be boxed
* @param id
* @returns
*/
addText(topX: number, topY: number, text: string, formatting?: {
wrapAt?: number;
width?: number;
height?: number;
textAlign?: "left" | "center" | "right";
box?: boolean | "box" | "blob" | "ellipse" | "diamond";
boxPadding?: number;
boxStrokeColor?: string;
textVerticalAlign?: "top" | "middle" | "bottom";
}, id?: string): string;
/**
*
* @param points
* @returns
*/
addLine(points: [[x: number, y: number]]): string;
/**
*
* @param points
* @param formatting
* @returns
*/
addArrow(points: [x: number, y: number][], formatting?: {
startArrowHead?: string;
endArrowHead?: string;
startObjectId?: string;
endObjectId?: string;
}): string;
/**
*
* @param topX
* @param topY
* @param imageFile
* @returns
*/
addImage(topX: number, topY: number, imageFile: TFile | string, scale?: boolean, //default is true which will scale the image to MAX_IMAGE_SIZE, false will insert image at 100% of its size
anchor?: boolean): Promise<string>;
/**
*
* @param topX
* @param topY
* @param tex
* @returns
*/
addLaTex(topX: number, topY: number, tex: string): Promise<string>;
/**
*
* @param objectA
* @param connectionA type ConnectionPoint = "top" | "bottom" | "left" | "right" | null
* @param objectB
* @param connectionB when passed null, Excalidraw will automatically decide
* @param formatting
* numberOfPoints: points on the line. Default is 0 ie. line will only have a start and end point
* startArrowHead: "triangle"|"dot"|"arrow"|"bar"|null
* endArrowHead: "triangle"|"dot"|"arrow"|"bar"|null
* padding:
* @returns
*/
connectObjects(objectA: string, connectionA: ConnectionPoint | null, objectB: string, connectionB: ConnectionPoint | null, formatting?: {
numberOfPoints?: number;
startArrowHead?: "triangle" | "dot" | "arrow" | "bar" | null;
endArrowHead?: "triangle" | "dot" | "arrow" | "bar" | null;
padding?: number;
}): string;
/**
* Adds a text label to a line or arrow. Currently only works with a straight (2 point - start & end - line)
* @param lineId id of the line or arrow object in elementsDict
* @param label the label text
* @returns undefined (if unsuccessful) or the id of the new text element
*/
addLabelToLine(lineId: string, label: string): string;
/**
* clear elementsDict and imagesDict only
*/
clear(): void;
/**
* clear() + reset all style values to default
*/
reset(): void;
/**
* returns true if MD file is an Excalidraw file
* @param f
* @returns
*/
isExcalidrawFile(f: TFile): boolean;
targetView: ExcalidrawView;
/**
* sets the target view for EA. All the view operations and the access to Excalidraw API will be performend on this view
* if view is null or undefined, the function will first try setView("active"), then setView("first").
* @param view
* @returns targetView
*/
setView(view?: ExcalidrawView | "first" | "active"): ExcalidrawView;
/**
*
* @returns https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw#ref
*/
getExcalidrawAPI(): any;
/**
* get elements in View
* @returns
*/
getViewElements(): ExcalidrawElement[];
/**
*
* @param elToDelete
* @returns
*/
deleteViewElements(elToDelete: ExcalidrawElement[]): boolean;
/**
* get the selected element in the view, if more are selected, get the first
* @returns
*/
getViewSelectedElement(): any;
/**
*
* @returns
*/
getViewSelectedElements(): any[];
/**
*
* @param el
* @returns TFile file handle for the image element
*/
getViewFileForImageElement(el: ExcalidrawElement): TFile | null;
/**
* copies elements from view to elementsDict for editing
* @param elements
*/
copyViewElementsToEAforEditing(elements: ExcalidrawElement[]): void;
/**
*
* @param forceViewMode
* @returns
*/
viewToggleFullScreen(forceViewMode?: boolean): void;
setViewModeEnabled(enabled: boolean): void;
/**
* This function gives you a more hands on access to Excalidraw.
* @param scene - The scene you want to load to Excalidraw
* @param restore - Use this if the scene includes legacy excalidraw file elements that need to be converted to the latest excalidraw data format (not a typical usecase)
* @returns
*/
viewUpdateScene(scene: {
elements?: ExcalidrawElement[];
appState?: AppState;
files?: BinaryFileData;
commitToHistory?: boolean;
storeAction?: "capture" | "none" | "update";
}, restore?: boolean): void;
/**
* connect an object to the selected element in the view
* @param objectA ID of the element
* @param connectionA
* @param connectionB
* @param formatting
* @returns
*/
connectObjectWithViewSelectedElement(objectA: string, connectionA: ConnectionPoint | null, connectionB: ConnectionPoint | null, formatting?: {
numberOfPoints?: number;
startArrowHead?: "triangle" | "dot" | "arrow" | "bar" | null;
endArrowHead?: "triangle" | "dot" | "arrow" | "bar" | null;
padding?: number;
}): boolean;
/**
* zoom tarteView to fit elements provided as input
* elements === [] will zoom to fit the entire scene
* selectElements toggles whether the elements should be in a selected state at the end of the operation
* @param selectElements
* @param elements
*/
viewZoomToElements(selectElements: boolean, elements: ExcalidrawElement[]): void;
/**
* Adds elements from elementsDict to the current view
* @param repositionToCursor default is false
* @param save default is true
* @param newElementsOnTop controls whether elements created with ExcalidrawAutomate
* are added at the bottom of the stack or the top of the stack of elements already in the view
* Note that elements copied to the view with copyViewElementsToEAforEditing retain their
* position in the stack of elements in the view even if modified using EA
* default is false, i.e. the new elements get to the bottom of the stack
* @param shouldRestoreElements - restore elements - auto-corrects broken, incomplete or old elements included in the update
* @returns
*/
addElementsToView(repositionToCursor?: boolean, save?: boolean, newElementsOnTop?: boolean, shouldRestoreElements?: boolean): Promise<boolean>;
/**
* Register instance of EA to use for hooks with TargetView
* By default ExcalidrawViews will check window.ExcalidrawAutomate for event hooks.
* Using this event you can set a different instance of Excalidraw Automate for hooks
* @returns true if successful
*/
registerThisAsViewEA(): boolean;
/**
* Sets the targetView EA to window.ExcalidrawAutomate
* @returns true if successful
*/
deregisterThisAsViewEA(): boolean;
/**
* If set, this callback is triggered when the user closes an Excalidraw view.
*/
onViewUnloadHook: (view: ExcalidrawView) => void;
/**
* If set, this callback is triggered, when the user changes the view mode.
* You can use this callback in case you want to do something additional when the user switches to view mode and back.
*/
onViewModeChangeHook: (isViewModeEnabled: boolean, view: ExcalidrawView, ea: ExcalidrawAutomate) => void;
/**
* If set, this callback is triggered, when the user hovers a link in the scene.
* You can use this callback in case you want to do something additional when the onLinkHover event occurs.
* This callback must return a boolean value.
* In case you want to prevent the excalidraw onLinkHover action you must return false, it will stop the native excalidraw onLinkHover management flow.
*/
onLinkHoverHook: (element: NonDeletedExcalidrawElement, linkText: string, view: ExcalidrawView, ea: ExcalidrawAutomate) => boolean;
/**
* If set, this callback is triggered, when the user clicks a link in the scene.
* You can use this callback in case you want to do something additional when the onLinkClick event occurs.
* This callback must return a boolean value.
* In case you want to prevent the excalidraw onLinkClick action you must return false, it will stop the native excalidraw onLinkClick management flow.
*/
onLinkClickHook: (element: ExcalidrawElement, linkText: string, event: MouseEvent, view: ExcalidrawView, ea: ExcalidrawAutomate) => boolean;
/**
* If set, this callback is triggered, when Excalidraw receives an onDrop event.
* You can use this callback in case you want to do something additional when the onDrop event occurs.
* This callback must return a boolean value.
* In case you want to prevent the excalidraw onDrop action you must return false, it will stop the native excalidraw onDrop management flow.
*/
onDropHook: (data: {
ea: ExcalidrawAutomate;
event: React.DragEvent<HTMLDivElement>;
draggable: any;
type: "file" | "text" | "unknown";
payload: {
files: TFile[];
text: string;
};
excalidrawFile: TFile;
view: ExcalidrawView;
pointerPosition: {
x: number;
y: number;
};
}) => boolean;
/**
* If set, this callback is triggered, when Excalidraw receives an onPaste event.
* You can use this callback in case you want to do something additional when the
* onPaste event occurs.
* This callback must return a boolean value.
* In case you want to prevent the excalidraw onPaste action you must return false,
* it will stop the native excalidraw onPaste management flow.
*/
onPasteHook: (data: {
ea: ExcalidrawAutomate;
payload: ClipboardData;
event: ClipboardEvent;
excalidrawFile: TFile;
view: ExcalidrawView;
pointerPosition: {
x: number;
y: number;
};
}) => boolean;
/**
* if set, this callback is triggered, when an Excalidraw file is opened
* You can use this callback in case you want to do something additional when the file is opened.
* This will run before the file level script defined in the `excalidraw-onload-script` frontmatter.
*/
onFileOpenHook: (data: {
ea: ExcalidrawAutomate;
excalidrawFile: TFile;
view: ExcalidrawView;
}) => Promise<void>;
/**
* if set, this callback is triggered, when an Excalidraw file is created
* see also: https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1124
*/
onFileCreateHook: (data: {
ea: ExcalidrawAutomate;
excalidrawFile: TFile;
view: ExcalidrawView;
}) => Promise<void>;
/**
* If set, this callback is triggered whenever the active canvas color changes
*/
onCanvasColorChangeHook: (ea: ExcalidrawAutomate, view: ExcalidrawView, //the excalidraw view
color: string) => void;
/**
* utility function to generate EmbeddedFilesLoader object
* @param isDark
* @returns
*/
getEmbeddedFilesLoader(isDark?: boolean): EmbeddedFilesLoader;
/**
* utility function to generate ExportSettings object
* @param withBackground
* @param withTheme
* @returns
*/
getExportSettings(withBackground: boolean, withTheme: boolean): ExportSettings;
/**
* get bounding box of elements
* bounding box is the box encapsulating all of the elements completely
* @param elements
* @returns
*/
getBoundingBox(elements: ExcalidrawElement[]): {
topX: number;
topY: number;
width: number;
height: number;
};
/**
* elements grouped by the highest level groups
* @param elements
* @returns
*/
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
* @param elements
* @returns
*/
getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;
/**
* @param element
* @param a
* @param b
* @param gap
* @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): Point[];
/**
* Gets the groupId for the group that contains all the elements, or null if such a group does not exist
* @param elements
* @returns null or the groupId
*/
getCommonGroupForElements(elements: ExcalidrawElement[]): string;
/**
* Gets all the elements from elements[] that share one or more groupIds with element.
* @param element
* @param elements - typically all the non-deleted elements in the scene
* @returns
*/
getElementsInTheSameGroupWithElement(element: ExcalidrawElement, elements: ExcalidrawElement[]): ExcalidrawElement[];
/**
* Gets all the elements from elements[] that are contained in the frame.
* @param element
* @param elements - typically all the non-deleted elements in the scene
* @returns
*/
getElementsInFrame(frameElement: ExcalidrawElement, elements: ExcalidrawElement[]): ExcalidrawElement[];
/**
* See OCR plugin for example on how to use scriptSettings
* Set by the ScriptEngine
*/
activeScript: string;
/**
*
* @returns script settings. Saves settings in plugin settings, under the activeScript key
*/
getScriptSettings(): {};
/**
* sets script settings.
* @param settings
* @returns
*/
setScriptSettings(settings: any): Promise<void>;
/**
* Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings
* @param file
* @param openState - if not provided {active: true} will be used
* @returns
*/
openFileInNewOrAdjacentLeaf(file: TFile, openState?: OpenViewState): WorkspaceLeaf;
/**
* measure text size based on current style settings
* @param text
* @returns
*/
measureText(text: string): {
width: number;
height: number;
};
/**
* Returns the size of the image element at 100% (i.e. the original size)
* @param imageElement an image element from the active scene on targetView
*/
getOriginalImageSize(imageElement: ExcalidrawImageElement): Promise<{
width: number;
height: number;
}>;
/**
* verifyMinimumPluginVersion returns true if plugin version is >= than required
* recommended use:
* if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.20")) {new Notice("message");return;}
* @param requiredVersion
* @returns
*/
verifyMinimumPluginVersion(requiredVersion: string): boolean;
/**
* Check if view is instance of ExcalidrawView
* @param view
* @returns
*/
isExcalidrawView(view: any): boolean;
/**
* sets selection in view
* @param elements
* @returns
*/
selectElementsInView(elements: ExcalidrawElement[] | string[]): void;
/**
* @returns an 8 character long random id
*/
generateElementId(): string;
/**
* @param element
* @returns a clone of the element with a new id
*/
cloneElement(element: ExcalidrawElement): ExcalidrawElement;
/**
* Moves the element to a specific position in the z-index
*/
moveViewElementToZIndex(elementId: number, newZIndex: number): void;
/**
* Deprecated. Use getCM / ColorMaster instead
* @param color
* @returns
*/
hexStringToRgb(color: string): number[];
/**
* Deprecated. Use getCM / ColorMaster instead
* @param color
* @returns
*/
rgbToHexString(color: number[]): string;
/**
* Deprecated. Use getCM / ColorMaster instead
* @param color
* @returns
*/
hslToRgb(color: number[]): number[];
/**
* Deprecated. Use getCM / ColorMaster instead
* @param color
* @returns
*/
rgbToHsl(color: number[]): number[];
/**
*
* @param color
* @returns
*/
colorNameToHex(color: string): string;
/**
* https://github.com/lbragile/ColorMaster
* @param color
* @returns
*/
getCM(color: TInput): ColorMaster;
importSVG(svgString: string): boolean;
}
export declare function initExcalidrawAutomate(plugin: ExcalidrawPlugin): Promise<ExcalidrawAutomate>;
export declare function destroyExcalidrawAutomate(): void;
export declare function _measureText(newText: string, fontSize: number, fontFamily: number, lineHeight: number): {
w: number;
h: number;
baseline: number;
};
export declare const generatePlaceholderDataURL: (width: number, height: number) => DataURL;
export declare function createPNG(templatePath: string, scale: number, exportSettings: ExportSettings, loader: EmbeddedFilesLoader, forceTheme: string, canvasTheme: string, canvasBackgroundColor: string, automateElements: ExcalidrawElement[], plugin: ExcalidrawPlugin, depth: number, padding?: number, imagesDict?: any): Promise<Blob>;
export declare function createSVG(templatePath: string, embedFont: boolean, exportSettings: ExportSettings, loader: EmbeddedFilesLoader, forceTheme: string, canvasTheme: string, canvasBackgroundColor: string, automateElements: ExcalidrawElement[], plugin: ExcalidrawPlugin, depth: number, padding?: number, imagesDict?: any, convertMarkdownLinksToObsidianURLs?: boolean): Promise<SVGSVGElement>;
export declare function estimateBounds(elements: ExcalidrawElement[]): [number, number, number, number];
export declare function repositionElementsToCursor(elements: ExcalidrawElement[], newPosition: {
x: number;
y: number;
}, center: boolean, api: ExcalidrawImperativeAPI): ExcalidrawElement[];
export declare const insertLaTeXToView: (view: ExcalidrawView) => void;
export declare const search: (view: ExcalidrawView) => Promise<void>;
/**
*
* @param elements
* @param query
* @param exactMatch - when searching for section header exactMatch should be set to true
* @returns the elements matching the query
*/
export declare const getTextElementsMatchingQuery: (elements: ExcalidrawElement[], query: string[], exactMatch?: boolean) => ExcalidrawElement[];
/**
*
* @param elements
* @param query
* @param exactMatch - when searching for section header exactMatch should be set to true
* @returns the elements matching the query
*/
export declare const getFrameElementsMatchingQuery: (elements: ExcalidrawElement[], query: string[], exactMatch?: boolean) => ExcalidrawElement[];
export declare const cloneElement: (el: ExcalidrawElement) => any;
export declare const verifyMinimumPluginVersion: (requiredVersion: string) => boolean;

View File

@@ -2,36 +2,110 @@
## Attributes and functions overview
Here's the interface implemented by ExcalidrawAutomate:
```typescript
export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
plugin: ExcalidrawPlugin;
targetView: ExcalidrawView = null; //the view currently edited
elementsDict: {[key:string]:any}; //contains the ExcalidrawElements currently edited in Automate indexed by el.id
imagesDict: {[key: FileId]: any}; //the images files including DataURL, indexed by fileId
mostRecentMarkdownSVG:SVGSVGElement = null; //Markdown renderer will drop a copy of the most recent SVG here for debugging purposes
style: {
strokeColor: string; //https://www.w3schools.com/colors/default.asp
backgroundColor: string;
angle: number; //radian
fillStyle: FillStyle; //type FillStyle = "hachure" | "cross-hatch" | "solid"
strokeWidth: number;
strokeStyle: StrokeStyle; //type StrokeStyle = "solid" | "dashed" | "dotted"
roughness: number;
opacity: number;
strokeSharpness: StrokeSharpness; //type StrokeSharpness = "round" | "sharp"
fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont
fontSize: number;
textAlign: string; //"left"|"right"|"center"
verticalAlign: string; //"top"|"bottom"|"middle" :for future use, has no effect currently
startArrowHead: string; //"triangle"|"dot"|"arrow"|"bar"|null
endArrowHead: string;
};
canvas: {
theme: string; //"dark"|"light"
viewBackgroundColor: string;
gridSize: number;
};
You can find the source file here: [ExcalidrawAutomate.d.ts](ExcalidrawAutomate.d.ts).
```javascript
/// <reference types="react" />
import ExcalidrawPlugin from "src/main";
import { FillStyle, StrokeStyle, ExcalidrawElement, ExcalidrawBindableElement, FileId, NonDeletedExcalidrawElement, ExcalidrawImageElement, StrokeRoundness, RoundnessType } from "@zsviczian/excalidraw/types/element/types";
import { Editor, OpenViewState, TFile, WorkspaceLeaf } from "obsidian";
import * as obsidian_module from "obsidian";
import ExcalidrawView, { ExportSettings } from "src/ExcalidrawView";
import { AppState, BinaryFileData, DataURL, ExcalidrawImperativeAPI, Point } from "@zsviczian/excalidraw/types/types";
import { EmbeddedFilesLoader } from "src/EmbeddedFileLoader";
import { ConnectionPoint, DeviceType } from "src/types";
import { ColorMaster } from "colormaster";
import { TInput } from "colormaster/types";
import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
import { PaneTarget } from "src/utils/ModifierkeyHelper";
export declare class ExcalidrawAutomate {
/**
* Utility function that returns the Obsidian Module object.
*/
get obsidian(): typeof obsidian_module;
get DEVICE(): DeviceType;
getAttachmentFilepath(filename: string): Promise<string>;
/**
* Prompts the user with a dialog to select new file action.
* - create markdown file
* - create excalidraw file
* - cancel action
* The new file will be relative to this.targetView.file.path, unless parentFile is provided.
* If shouldOpenNewFile is true, the new file will be opened in a workspace leaf.
* targetPane control which leaf will be used for the new file.
* Returns the TFile for the new file or null if the user cancelled the action.
* @param newFileNameOrPath
* @param shouldOpenNewFile
* @param targetPane //type PaneTarget = "active-pane"|"new-pane"|"popout-window"|"new-tab"|"md-properties";
* @param parentFile
* @returns
*/
newFilePrompt(newFileNameOrPath: string, shouldOpenNewFile: boolean, targetPane?: PaneTarget, parentFile?: TFile): Promise<TFile | null>;
/**
* Generates a new Obsidian Leaf following Excalidraw plugin settings such as open in Main Workspace or not, open in adjacent pane if available, etc.
* @param origo // the currently active leaf, the origin of the new leaf
* @param targetPane //type PaneTarget = "active-pane"|"new-pane"|"popout-window"|"new-tab"|"md-properties";
* @returns
*/
getLeaf(origo: WorkspaceLeaf, targetPane?: PaneTarget): WorkspaceLeaf;
/**
* Returns the editor or leaf.view of the currently active embedded obsidian file.
* If view is not provided, ea.targetView is used.
* If the embedded file is a markdown document the function will return
* {file:TFile, editor:Editor} otherwise it will return {view:any}. You can check view type with view.getViewType();
* @param view
* @returns
*/
getActiveEmbeddableViewOrEditor(view?: ExcalidrawView): {
view: any;
} | {
file: TFile;
editor: Editor;
} | null;
plugin: ExcalidrawPlugin;
elementsDict: {
[key: string]: any;
};
imagesDict: {
[key: FileId]: any;
};
mostRecentMarkdownSVG: SVGSVGElement;
style: {
strokeColor: string;
backgroundColor: string;
angle: number;
fillStyle: FillStyle;
strokeWidth: number;
strokeStyle: StrokeStyle;
roughness: number;
opacity: number;
strokeSharpness?: StrokeRoundness;
roundness: null | {
type: RoundnessType;
value?: number;
};
fontFamily: number;
fontSize: number;
textAlign: string;
verticalAlign: string;
startArrowHead: string;
endArrowHead: string;
};
canvas: {
theme: string;
viewBackgroundColor: string;
gridSize: number;
};
colorPalette: {};
constructor(plugin: ExcalidrawPlugin, view?: ExcalidrawView);
/**
*
* @returns the last recorded pointer position on the Excalidraw canvas
*/
getViewLastPointerPosition(): {
x: number;
y: number;
};
/**
*
* @returns
@@ -71,9 +145,17 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
* @param templatePath
*/
toClipboard(templatePath?: string): Promise<void>;
/**
* @param file: TFile
* @returns ExcalidrawScene
*/
getSceneFromFile(file: TFile): Promise<{
elements: ExcalidrawElement[];
appState: AppState;
}>;
/**
* get all elements from ExcalidrawAutomate elementsDict
* @returns elements from elemenetsDict
* @returns elements from elementsDict
*/
getElements(): ExcalidrawElement[];
/**
@@ -101,10 +183,14 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
"excalidraw-url-prefix"?: string;
"excalidraw-export-transparent"?: boolean;
"excalidraw-export-dark"?: boolean;
"excalidraw-export-svgpadding"?: number;
"excalidraw-export-padding"?: number;
"excalidraw-export-pngscale"?: number;
"excalidraw-default-mode"?: "view" | "zen";
"excalidraw-onload-script"?: string;
"excalidraw-linkbutton-opacity"?: number;
"excalidraw-autoexport"?: boolean;
};
plaintext?: string;
}): Promise<string>;
/**
*
@@ -134,6 +220,16 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
*/
wrapText(text: string, lineLen: number): string;
private boxedElement;
addIFrame(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string;
/**
*
* @param topX
* @param topY
* @param width
* @param height
* @returns
*/
addEmbeddable(topX: number, topY: number, width: number, height: number, url?: string, file?: TFile): string;
/**
*
* @param topX
@@ -170,6 +266,11 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
* @returns
*/
addBlob(topX: number, topY: number, width: number, height: number): string;
/**
* Refresh the size of a text element to fit its contents
* @param id - the id of the text element
*/
refreshTextElementSize(id: string): void;
/**
*
* @param topX
@@ -184,9 +285,11 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
wrapAt?: number;
width?: number;
height?: number;
textAlign?: string;
textAlign?: "left" | "center" | "right";
box?: boolean | "box" | "blob" | "ellipse" | "diamond";
boxPadding?: number;
boxStrokeColor?: string;
textVerticalAlign?: "top" | "middle" | "bottom";
}, id?: string): string;
/**
*
@@ -213,7 +316,8 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
* @param imageFile
* @returns
*/
addImage(topX: number, topY: number, imageFile: TFile): Promise<string>;
addImage(topX: number, topY: number, imageFile: TFile | string, scale?: boolean, //default is true which will scale the image to MAX_IMAGE_SIZE, false will insert image at 100% of its size
anchor?: boolean): Promise<string>;
/**
*
* @param topX
@@ -262,12 +366,14 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
* @returns
*/
isExcalidrawFile(f: TFile): boolean;
targetView: ExcalidrawView;
/**
*
* sets the target view for EA. All the view operations and the access to Excalidraw API will be performend on this view
* if view is null or undefined, the function will first try setView("active"), then setView("first").
* @param view
* @returns
* @returns targetView
*/
setView(view: ExcalidrawView | "first" | "active"): ExcalidrawView;
setView(view?: ExcalidrawView | "first" | "active"): ExcalidrawView;
/**
*
* @returns https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw#ref
@@ -311,6 +417,19 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
* @returns
*/
viewToggleFullScreen(forceViewMode?: boolean): void;
setViewModeEnabled(enabled: boolean): void;
/**
* This function gives you a more hands on access to Excalidraw.
* @param scene - The scene you want to load to Excalidraw
* @param restore - Use this if the scene includes legacy excalidraw file elements that need to be converted to the latest excalidraw data format (not a typical usecase)
* @returns
*/
viewUpdateScene(scene: {
elements?: ExcalidrawElement[];
appState?: AppState;
files?: BinaryFileData;
commitToHistory?: boolean;
}, restore?: boolean): void;
/**
* connect an object to the selected element in the view
* @param objectA ID of the element
@@ -325,6 +444,14 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
endArrowHead?: "triangle" | "dot" | "arrow" | "bar" | null;
padding?: number;
}): boolean;
/**
* zoom tarteView to fit elements provided as input
* elements === [] will zoom to fit the entire scene
* selectElements toggles whether the elements should be in a selected state at the end of the operation
* @param selectElements
* @param elements
*/
viewZoomToElements(selectElements: boolean, elements: ExcalidrawElement[]): void;
/**
* Adds elements from elementsDict to the current view
* @param repositionToCursor default is false
@@ -334,9 +461,10 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
* Note that elements copied to the view with copyViewElementsToEAforEditing retain their
* position in the stack of elements in the view even if modified using EA
* default is false, i.e. the new elements get to the bottom of the stack
* @param shouldRestoreElements - restore elements - auto-corrects broken, incomplete or old elements included in the update
* @returns
*/
addElementsToView(repositionToCursor?: boolean, save?: boolean, newElementsOnTop?: boolean): Promise<boolean>;
addElementsToView(repositionToCursor?: boolean, save?: boolean, newElementsOnTop?: boolean, shouldRestoreElements?: boolean): Promise<boolean>;
/**
* Register instance of EA to use for hooks with TargetView
* By default ExcalidrawViews will check window.ExcalidrawAutomate for event hooks.
@@ -362,7 +490,7 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
* If set, this callback is triggered, when the user hovers a link in the scene.
* You can use this callback in case you want to do something additional when the onLinkHover event occurs.
* This callback must return a boolean value.
* In case you want to prevent the excalidraw onLinkHover action you must return true, it will stop the native excalidraw onLinkHover management flow.
* In case you want to prevent the excalidraw onLinkHover action you must return false, it will stop the native excalidraw onLinkHover management flow.
*/
onLinkHoverHook: (element: NonDeletedExcalidrawElement, linkText: string, view: ExcalidrawView, ea: ExcalidrawAutomate) => boolean;
/**
@@ -394,6 +522,49 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
y: number;
};
}) => boolean;
/**
* If set, this callback is triggered, when Excalidraw receives an onPaste event.
* You can use this callback in case you want to do something additional when the
* onPaste event occurs.
* This callback must return a boolean value.
* In case you want to prevent the excalidraw onPaste action you must return false,
* it will stop the native excalidraw onPaste management flow.
*/
onPasteHook: (data: {
ea: ExcalidrawAutomate;
payload: ClipboardData;
event: ClipboardEvent;
excalidrawFile: TFile;
view: ExcalidrawView;
pointerPosition: {
x: number;
y: number;
};
}) => boolean;
/**
* if set, this callback is triggered, when an Excalidraw file is opened
* You can use this callback in case you want to do something additional when the file is opened.
* This will run before the file level script defined in the `excalidraw-onload-script` frontmatter.
*/
onFileOpenHook: (data: {
ea: ExcalidrawAutomate;
excalidrawFile: TFile;
view: ExcalidrawView;
}) => Promise<void>;
/**
* if set, this callback is triggered, when an Excalidraw file is created
* see also: https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1124
*/
onFileCreateHook: (data: {
ea: ExcalidrawAutomate;
excalidrawFile: TFile;
view: ExcalidrawView;
}) => Promise<void>;
/**
* If set, this callback is triggered whenever the active canvas color changes
*/
onCanvasColorChangeHook: (ea: ExcalidrawAutomate, view: ExcalidrawView, //the excalidraw view
color: string) => void;
/**
* utility function to generate EmbeddedFilesLoader object
* @param isDark
@@ -431,6 +602,15 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
* @returns
*/
getLargestElement(elements: ExcalidrawElement[]): ExcalidrawElement;
/**
* @param element
* @param a
* @param b
* @param gap
* @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): Point[];
/**
* Gets the groupId for the group that contains all the elements, or null if such a group does not exist
* @param elements
@@ -445,14 +625,12 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
*/
getElementsInTheSameGroupWithElement(element: ExcalidrawElement, elements: ExcalidrawElement[]): ExcalidrawElement[];
/**
* Gets all the elements from elements[] that are contained in the frame.
* @param element
* @param a
* @param b
* @param gap
* @returns 2 or 0 intersection points between line going through `a` and `b`
* and the `element`, in ascending order of distance from `a`.
* @param elements - typically all the non-deleted elements in the scene
* @returns
*/
intersectElementWithLine(element: ExcalidrawBindableElement, a: readonly [number, number], b: readonly [number, number], gap?: number): Point[];
getElementsInFrame(frameElement: ExcalidrawElement, elements: ExcalidrawElement[]): ExcalidrawElement[];
/**
* See OCR plugin for example on how to use scriptSettings
* Set by the ScriptEngine
@@ -472,9 +650,10 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
/**
* Open a file in a new workspaceleaf or reuse an existing adjacent leaf depending on Excalidraw Plugin Settings
* @param file
* @param openState - if not provided {active: true} will be used
* @returns
*/
openFileInNewOrAdjacentLeaf(file: TFile): WorkspaceLeaf;
openFileInNewOrAdjacentLeaf(file: TFile, openState?: OpenViewState): WorkspaceLeaf;
/**
* measure text size based on current style settings
* @param text
@@ -484,6 +663,14 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
width: number;
height: number;
};
/**
* Returns the size of the image element at 100% (i.e. the original size)
* @param imageElement an image element from the active scene on targetView
*/
getOriginalImageSize(imageElement: ExcalidrawImageElement): Promise<{
width: number;
height: number;
}>;
/**
* verifyMinimumPluginVersion returns true if plugin version is >= than required
* recommended use:
@@ -503,7 +690,7 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
* @param elements
* @returns
*/
selectElementsInView(elements: ExcalidrawElement[]): void;
selectElementsInView(elements: ExcalidrawElement[] | string[]): void;
/**
* @returns an 8 character long random id
*/
@@ -518,25 +705,25 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
*/
moveViewElementToZIndex(elementId: number, newZIndex: number): void;
/**
*
* Deprecated. Use getCM / ColorMaster instead
* @param color
* @returns
*/
hexStringToRgb(color: string): number[];
/**
*
* Deprecated. Use getCM / ColorMaster instead
* @param color
* @returns
*/
rgbToHexString(color: number[]): string;
/**
*
* Deprecated. Use getCM / ColorMaster instead
* @param color
* @returns
*/
hslToRgb(color: number[]): number[];
/**
*
* Deprecated. Use getCM / ColorMaster instead
* @param color
* @returns
*/
@@ -547,5 +734,47 @@ export declare class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
* @returns
*/
colorNameToHex(color: string): string;
}```
/**
* https://github.com/lbragile/ColorMaster
* @param color
* @returns
*/
getCM(color: TInput): ColorMaster;
importSVG(svgString: string): boolean;
}
export declare function initExcalidrawAutomate(plugin: ExcalidrawPlugin): Promise<ExcalidrawAutomate>;
export declare function destroyExcalidrawAutomate(): void;
export declare function _measureText(newText: string, fontSize: number, fontFamily: number, lineHeight: number): {
w: number;
h: number;
baseline: number;
};
export declare const generatePlaceholderDataURL: (width: number, height: number) => DataURL;
export declare function createPNG(templatePath: string, scale: number, exportSettings: ExportSettings, loader: EmbeddedFilesLoader, forceTheme: string, canvasTheme: string, canvasBackgroundColor: string, automateElements: ExcalidrawElement[], plugin: ExcalidrawPlugin, depth: number, padding?: number, imagesDict?: any): Promise<Blob>;
export declare function createSVG(templatePath: string, embedFont: boolean, exportSettings: ExportSettings, loader: EmbeddedFilesLoader, forceTheme: string, canvasTheme: string, canvasBackgroundColor: string, automateElements: ExcalidrawElement[], plugin: ExcalidrawPlugin, depth: number, padding?: number, imagesDict?: any, convertMarkdownLinksToObsidianURLs?: boolean): Promise<SVGSVGElement>;
export declare function estimateBounds(elements: ExcalidrawElement[]): [number, number, number, number];
export declare function repositionElementsToCursor(elements: ExcalidrawElement[], newPosition: {
x: number;
y: number;
}, center: boolean, api: ExcalidrawImperativeAPI): ExcalidrawElement[];
export declare const insertLaTeXToView: (view: ExcalidrawView) => void;
export declare const search: (view: ExcalidrawView) => Promise<void>;
/**
*
* @param elements
* @param query
* @param exactMatch - when searching for section header exactMatch should be set to true
* @returns the elements matching the query
*/
export declare const getTextElementsMatchingQuery: (elements: ExcalidrawElement[], query: string[], exactMatch?: boolean) => ExcalidrawElement[];
/**
*
* @param elements
* @param query
* @param exactMatch - when searching for section header exactMatch should be set to true
* @returns the elements matching the query
*/
export declare const getFrameElementsMatchingQuery: (elements: ExcalidrawElement[], query: string[], exactMatch?: boolean) => ExcalidrawElement[];
export declare const cloneElement: (el: ExcalidrawElement) => any;
export declare const verifyMinimumPluginVersion: (requiredVersion: string) => boolean;
```

View File

@@ -88,4 +88,4 @@ 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.
This is relevant when using the `addArrow()` and the `connectObjects()` functions.

View File

@@ -21,7 +21,7 @@ The second line resets ExcalidrawAutomate to defaults. This is important as you
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
### Before we dive deeper, here are three 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.

View File

@@ -22,7 +22,7 @@ Places the generated drawing to the clipboard. Useful when you don't want to cre
```typescript
getElements():ExcalidrawElement[];
```
Returns the elements in ExcalidrawAutomate as an array of ExcalidrawElements. This format is usefull when working with ExcalidrawRef.
Returns the elements in ExcalidrawAutomate as an array of ExcalidrawElements. This format is useful when working with ExcalidrawRef.
### getElement()
```typescript
@@ -156,7 +156,7 @@ 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.
Note: you can call `getExcalidrawAPI().getSceneElements()` to retrieve all the elements in the scene.
#### viewToggleFullScreen()
```typescript
@@ -178,7 +178,7 @@ Same as `connectObjects()`, but ObjectB is the currently selected element in the
async addElementsToView(repositionToCursor:boolean=false, save:boolean=false):Promise<boolean>
```
Adds elements created with ExcalidrawAutomate to the target ExcalidrawView.
`repositionToCursor` dafault is false
`repositionToCursor` default 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.
@@ -204,7 +204,7 @@ onDropHook (data: {
```
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.
The function should return a boolean value. True if the drop was handled by the hook and further 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

View File

@@ -1,6 +1,6 @@
# [◀ 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.
This is a slightly more elaborate example. This will generate 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)

View File

@@ -21,7 +21,7 @@ This will allow you to assign hotkeys to your favorite scripts just like to any
## 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.
- `ea`: The Script Engine 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, buttons?: [{caption:string, action:Function}])`
- Opens a prompt that asks for an input. Returns a string with the input.

View File

@@ -8,7 +8,7 @@ With a little work, using ExcalidrawAutomate you can generate simple mindmaps, b
## 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)
- [Element Style](API/element_style.md)
- [Canvas Style](API/canvas_style.md)
- [Adding Objects](API/objects.md)
- [Utility Functions](API/utility.md)

484
docs/zh-cn/AutomateHowTo.md Normal file
View File

@@ -0,0 +1,484 @@
# Excalidraw 自动化使用指南
> 此说明当前更新至 `5569cff`。
[English](../../AutomateHowTo.md)
Excalidraw 自动化允许您使用 [Templater](https://github.com/SilentVoid13/Templater) 插件创建 Excalidraw 绘图。
通过一些工作,使用 Excalidraw 自动化,您可以根据保管库中的文档生成简单的思维导图、填写 SVG 表单、创建自定义图表等。
您可以通过 ExcalidrawAutomate 对象访问 Excalidraw 自动化。我建议您以以下代码开始您的自动化脚本。
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 Obsidian 中!*
```javascript
const ea = ExcalidrawAutomate;
ea.reset();
```
第一行创建了一个实用的常量,这样您就可以避免写 100 次 `ExcalidrawAutomate`
第二行将 `ExcalidrawAutomate` 重置为默认值。这一点很重要,因为您将不知道之前执行了哪个模板,因此您也不知道 `Excalidraw` 的状态。
## 使用 Excalidraw 自动化的基本逻辑
1. 设置您想要绘制的元素的样式
2. 添加元素。每添加一个新元素,它都会在上一个元素的上方添加一层,因此在重叠对象的情况下,后添加的元素会在前一个元素之上。
3. 调用 `await ea.create();` 来实例化绘图
您可以在添加不同元素之间更改样式。我将元素样式与创建分开是基于这样的假设:您可能会设置描边颜色、描边样式、描边粗糙度等,并使用这些设置绘制大多数元素。每次添加元素时设置所有这些参数是没有意义的。
### 在深入探讨之前,这里有两个简单的示例脚本
#### 使用模板在自定义文件夹中创建具有自定义名称的新绘图
这个简单的脚本为您提供了比 Excalidraw 插件设置更大的灵活性,可以为您的绘图命名、将其放入文件夹中,并应用模板。
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 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
});
%>
```
#### 创建一个简单的绘图
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 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();
%>
```
该脚本将生成以下绘图:
![FristDemo](https://user-images.githubusercontent.com/14358394/116825643-6e5a8b00-ab90-11eb-9e3a-37c524620d0d.png)
## 属性和功能一览
这是 ExcalidrawAutomate 实现的接口:
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 Obsidian 中!*
```javascript
ExcalidrawAutomate: {
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;
};
```
## 元素样式
正如您所注意到的,某些样式具有设置函数。这是为了帮助您浏览属性的可用值。不过,您并不需要使用设置函数,您也可以直接设置值。
### strokeColor
字符串。线条的颜色。[CSS 合法颜色值](https://www.w3schools.com/cssref/css_colors_legal.asp)
允许的值包括 [HTML 颜色名称](https://www.w3schools.com/colors/colors_names.asp)、十六进制 RGB 字符串,例如 `#FF0000` 表示红色。
### backgroundColor
字符串。对象的填充颜色。[CSS 合法颜色值](https://www.w3schools.com/cssref/css_colors_legal.asp)
允许的值包括 [HTML 颜色名称](https://www.w3schools.com/colors/colors_names.asp)、十六进制 RGB 字符串,例如 `#FF0000` 表示红色,或 `transparent`(透明)。
### angle
数字。以弧度表示的旋转。90° == `Math.PI/2`
### fillStyle, setFillStyle()
```typescript
type FillStyle = "hachure" | "cross-hatch" | "solid";
setFillStyle (val:number);
```
fillStyle 是一个字符串.
`setFillStyle()` 接受一个数字:
- 0: "hachure"(斜线填充)
- 1: "cross-hatch"(交叉斜线填充)
- 其他任何数字: "solid"(实心填充)
### strokeWidth
数字,设置描边的宽度。
### strokeStyle, setStrokeStyle()
```typescript
type StrokeStyle = "solid" | "dashed" | "dotted";
setStrokeStyle (val:number);
```
strokeStyle 是一个字符串。
`setStrokeStyle()` 接受一个数字:
- 0: "solid"(实线)
- 1: "dashed"(虚线)
- 其他任何数字: "dotted"(点线)
### roughness
数字。在 Excalidraw 中称为“粗糙度”。接受三个值:
- 0: 建筑师
- 1: 艺术家
- 2: 卡通画家
### opacity
介于 0 和 100 之间的数字。对象的透明度,包括描边和填充。
### strokeSharpness, setStrokeSharpness()
```typescript
type StrokeSharpness = "round" | "sharp";
setStrokeSharpness(val:nmuber);
```
strokeSharpness 是一个字符串。
“round” 线条是曲线“sharp” 线条在转折点处断开(硬弯折)。
`setStrokeSharpness()` 接受一个数字:
- 0: "round"(圆滑)
- 其他任何数字: "sharp"(尖锐)
### fontFamily, setFontFamily()
数字。有效值为 1、2 和 3。
`setFontFamily()` 也会接受一个数字并返回字体名称。
- 1: "Virgil, Segoe UI Emoji"
- 2: "Helvetica, Segoe UI Emoji"
- 3: "Cascadia, Segoe UI Emoji"
### fontSize
数字。默认值为 20 像素。
### textAlign
字符串。文本的水平对齐方式。有效值为 "left"(左对齐)、"center"(居中对齐)、"right"(右对齐)。
在使用 `addText()` 函数设置固定宽度时,这一点很重要。
### verticalAlign
字符串。文本的垂直对齐方式。有效值为 "top"(顶部)和 "middle"(中间)。
在使用 `addText()` 函数设置固定高度时,这一点很重要。
### startArrowHead, endArrowHead
字符串。有效值为 "arrow"(箭头)、"bar"(线条)、"dot"(点)和 "none"(无)。指定箭头的起始和结束。
在使用 `addArrow()``connectObjects()` 函数时,这一点很重要。
## canvas
设置画布的属性。
### theme, setTheme()
字符串。有效值为 "light"(明亮)和 "dark"(黑暗)。
`setTheme()` 接受一个数字:
- 0: "light"(明亮)
- 其他任何数字: "dark"(黑暗)
### viewBackgroundColor
字符串。对象的填充颜色。[CSS 合法颜色值](https://www.w3schools.com/cssref/css_colors_legal.asp)
允许的值包括 [HTML 颜色名称](https://www.w3schools.com/colors/colors_names.asp)、十六进制 RGB 字符串,例如 `#FF0000` 表示红色,或 `transparent`(透明)。
## 添加对象
这些函数将向您的绘图中添加对象。画布是无限的,接受负值和正值的 X 和 Y 坐标。X 值从左到右增加Y 值从上到下增加。
![坐标](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
```
返回对象的 `id`。在用线连接对象时,需要使用 `id`。请参见后文。
### addText
```typescript
addText(topX:number, topY:number, text:string, formatting?:{width:number, height:number,textAlign: string, verticalAlign:string, box: boolean, boxPadding: number}):string
```
向绘图中添加文本。
格式参数是可选的:
- 如果未指定 `width`(宽度)和 `height`(高度),函数将根据 `fontFamily``fontSize` 和提供的文本计算宽度和高度。
- 如果您希望文本相对于绘图中的其他元素居中,可以提供固定的高度和宽度,同时可以指定 `textAlign``verticalAlign`,如上所述。例如:`{width:500, textAlign:"center"}`
- 如果您想在文本周围添加一个框,请设置 `{box:true}`
返回对象的 `id`。在用线连接对象时,需要使用 `id`。请参见后文。如果 `{box:true}`,则返回包围框的 `id`
### addLine()
```typescript
addLine(points: [[x:number,y:number]]):void
```
添加一条连接提供的点的线。必须至少包含两个点 `points.length >= 2`。如果提供的点超过两个,间隔点将作为断点添加。如果 `strokeSharpness` 设置为 "sharp",线条将在转折处断开;如果设置为 "round",线条将是曲线。
### addArrow()
```typescript
addArrow(points: [[x:number,y:number]],formatting?:{startArrowHead:string,endArrowHead:string,startObjectId:string,endObjectId:string}):void
```
添加一条连接提供的点的箭头。必须至少包含两个点 `points.length >= 2`。如果提供的点超过两个,间隔点将作为断点添加。如果元素 `style.strokeSharpness` 设置为 "sharp",线条将在转折处断开;如果设置为 "round",线条将是曲线。
`startArrowHead``endArrowHead` 指定要使用的箭头类型,如上所述。有效值为 "none"(无)、"arrow"(箭头)、"dot"(点)和 "bar"(线条)。例如:`{startArrowHead: "dot", endArrowHead: "arrow"}`
`startObjectId``endObjectId` 是连接对象的对象 ID。我建议使用 `connectObjects` 而不是调用 `addArrow()` 来连接对象。
### 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
```
连接两个对象的箭头。
`objectA``objectB` 是字符串。这些是要连接的对象的 ID。这些 ID 是通过 `addRect()``addDiamond()``addEllipse()``addText()` 创建这些对象时返回的。
`connectionA``connectionB` 指定在对象上的连接位置。有效值为:"top"(上)、"bottom"(下)、"left"(左)和 "right"(右)。
`numberOfPoints` 设置线条的间隔断点数量。默认值为零,意味着箭头的起点和终点之间不会有断点。当在绘图中移动对象时,这些断点将影响 Excalidraw 如何重新调整线条。
`startArrowHead``endArrowHead` 的功能与 `addArrow()` 中描述的一致。
### addToGroup()
```typescript
addToGroup(objectIds:[]):void
```
`objectIds` 中列出的对象进行分组。
## Utility functions
### clear()
`clear()` 将从缓存中清除对象,但会保留元素样式设置。
### reset()
`reset()` 将首先调用 `clear()`,然后将元素样式重置为默认值。
### toClipboard()
```typescript
async toClipboard(templatePath?:string)
```
将生成的图形放入剪贴板。当您不想创建新图形,而是想将其他项目粘贴到现有图形上时,这非常有用。
### create()
```typescript
async create(params?:{filename: string, foldername:string, templatePath:string, onNewPane: boolean})
```
创建图形并打开它。
`filename` 是要创建的图形的文件名(不带扩展名)。如果为 `null`,则 Excalidraw 会生成一个文件名。
`foldername` 是文件应创建的文件夹。如果为 `null`,则将根据 Excalidraw 设置使用新图形的默认文件夹。
`templatePath` 是包含完整路径和扩展名的模板文件名。该模板文件将作为基础层添加,所有通过 ExcalidrawAutomate 添加的额外对象将出现在模板元素之上。如果为 `null`,则不使用模板,即空白图形将作为添加对象的基础。
`onNewPane` 定义新图形应创建的位置。`false` 将在当前活动的标签页中打开图形;`true` 将通过垂直分割当前标签页来打开图形。
示例:
```javascript
create({filename:"my drawing", foldername:"myfolder/subfolder/", templatePath: "Excalidraw/template.excalidraw", onNewPane: true});
```
### createSVG()
```typescript
async createSVG(templatePath?:string)
```
返回一个包含生成图形的 HTML `SVGSVGElement`
### createPNG()
```typescript
async createPNG(templatePath?:string)
```
返回一个包含生成图形的 PNG 图像的 blob。
## 示例
### 将新图形插入到当前编辑的文档中
此模板将提示您输入图形的标题。它将在您提供的标题下创建一个新图形,并在您正在编辑的文档的文件夹中。然后,它将在光标位置插入新图形,并通过分割当前标签页在新的工作区标签页中打开新图形。
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 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
});
%>
```
### 连接对象
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 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();
%>
```
### 使用模板
这个示例与第一个类似,但旋转了 90°并使用了模板同时指定了文件名和保存图形的文件夹并在新的标签页中打开新图形。
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 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});
%>
```
### 从文本大纲生成简单思维导图
这是一个稍微复杂一些的示例。这个示例将从一个表格化的大纲生成思维导图。
![Drawing 2021-05-05 20 52 34](https://user-images.githubusercontent.com/14358394/117194124-00a69d00-ade4-11eb-8b75-5e18a9cbc3cd.png)
输入示例:
```
- 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
```
The script:
*使用 <kbd>CTRL+Shift+V</kbd> 将代码粘贴到 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);
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});
%>
```

289
docs/zh-cn/README.md Normal file
View File

@@ -0,0 +1,289 @@
# Excalidraw
> 此说明当前更新至 `5569cff`。
[English](../../README.md)
👉👉👉 快来查看并为新的 [Obsidian-Excalidraw 社区维基](https://excalidraw-obsidian.online/Hobbies/Excalidraw+Blog/WIKI/Welcome+to+the+WIKI)贡献你的力量吧
Obsidian-Excalidraw 插件将 [Excalidraw](https://excalidraw.com/) 这一功能丰富的草图工具集成到 Obsidian 中。您可以在您的库中存储和编辑 Excalidraw 文件,可以将图形嵌入到文档中,还可以在 Excalidraw 中链接到文档和其他图形。有关 Excalidraw 功能的展示,请查看我的博客文章 [这里](https://www.zsolt.blog/2021/03/showcasing-excalidraw.html) 或观看以下视频。
## 视频演示
<a href="https://youtu.be/P_Q6avJGoWI" target="_blank"><img src="https://github.com/zsviczian/obsidian-excalidraw-plugin/assets/14358394/da34bb33-7610-45e6-b36f-cb7a02a9141b" width="300"/></a>
<a href="https://youtu.be/o0exK-xFP3k" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931370-aa4d88de-c4a8-46cc-aeb2-dc09aa0bea39.jpg" width="300"/></a>
<a href="https://youtu.be/QKnQgSjJVuc" target="_blank"><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/thumbnail-getting-started.jpg" width="300"/></a>
### 这是我完整的视频目录:
<a href="https://excalidraw-obsidian.online/Hobbies/Excalidraw+Blog/Catalogue+of+Videos"><img width="380" alt="image" src="https://github.com/zsviczian/obsidian-excalidraw-plugin/assets/14358394/2577e5ad-7a21-4c62-acd5-4fe80c8a8a95"></a>
<br>
<details><summary>10 部分(稍微过时)视频演示</summary>
<a href="https://youtu.be/sY4FoflGaiM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160304-7f211180-e17c-11eb-8363-c52723de1ffd.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;1 入门</a><br>
<a href="https://youtu.be/Iy_oVTq12Gw" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160312-8a743d00-e17c-11eb-9fa2-490ef4cbd59e.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;2 基本形状和功能</a><br>
<a href="https://youtu.be/QOL1KF7-kdc" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160323-96f89580-e17c-11eb-9bce-8eb1067a51bb.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;3 元素分组</a><br>
<a href="https://youtu.be/aSgcbfspvfo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160332-9f50d080-e17c-11eb-98e9-fec60fe147d9.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;4 模板库</a><br>
<a href="https://youtu.be/MaJ5jJwBRWs" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160341-a546b180-e17c-11eb-9de8-d87fdc844c9c.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;5 嵌入</a><br>
<a href="https://youtu.be/MXzeCOEExNo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160346-aa0b6580-e17c-11eb-930b-4024807040d1.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;6 链接</a><br>
<a href="https://youtu.be/R0IAg0s-wQE" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160354-b2fc3700-e17c-11eb-81af-9e71e461f6dd.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;7 Markdown</a><br>
<a href="https://youtu.be/ibdS7ykwpW4" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160360-b8f21800-e17c-11eb-8bd8-79d4e3f6e92d.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;8 模板</a><br>
<a href="https://youtu.be/VRZVujfVab0" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160367-bdb6cc00-e17c-11eb-92f1-6f59faea85fd.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;9 Excalidraw 自动化</a><br>
<a href="https://youtu.be/D1iBYo1_jjc" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/125160374-c3141680-e17c-11eb-8cc2-dfaffd903d15.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;10 杂项</a><br>
</details>
<details><summary>将内容嵌入 Excalidraw</summary>
<a href="https://www.youtube.com/watch?v=_c_0zpBJ4Xc&" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/138607067-ccb62f92-48a4-4880-ac6e-68c1bf86ac2c.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;图像元素</a><br>
<a href="https://youtu.be/r08wk-58DPk" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/143732412-1c65227e-4381-406d-847a-b001ab3506ca.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;LaTeX 演示</a><br>
<a href="https://youtu.be/tsecSfnTMow" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/143732440-90bfa029-8615-462e-ada3-c903d71a82c9.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Markdown 嵌入</a><br>
<a href="https://youtu.be/K6qZkTz8GHs" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/143783906-15cee494-c6d5-4495-a2ca-74634e4e7355.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Markdown 嵌入高级功能</a><br>
<a href="https://youtu.be/Etskjw7a5zo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931461-0979b821-315a-41dd-86f1-31d169b7c127.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;链接到元素、垂直文本对齐、Markdown 样式</a><br>
</details>
<details><summary>脚本引擎商店 - Excalidraw 自动化</summary>
<a href="https://youtu.be/hePJcObHIso" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/145684531-8d9c2992-59ac-4ebc-804a-4cce1777ded2.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;介绍脚本引擎</a><br>
<a href="https://youtu.be/lzYdOQ6z8F0" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/147889174-6c306d0d-2d29-46cc-a53f-3f0013cf14de.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;脚本引擎商店</a><br>
</details>
<details><summary>使用颜色</summary>
<a href="https://youtu.be/6PLGHBH9VZ4" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/194773147-5418a0ab-6be5-4eb0-a8e4-d6af21b1b483.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;颜色 - Excalidraw 基础(自定义)</a><br>
<a href="https://youtu.be/epYNx2FSf2w" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/194773211-9e871be7-0795-4dc7-947e-c6c275e690d0.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Excalidraw 调色板(自定义)</a><br>
<a href="https://youtu.be/Amhlv6r9WvM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/194773268-400cfb1b-6bde-45e0-9e4b-91bbaa461cf0.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;“艺术”颜色渐变</a><br>
<a href="https://youtu.be/r9oB1SlK1GU" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/194773527-ef35c8b9-1a6d-4415-9c7e-b667fb17535d.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;美丽草图的简单规则</a><br>
<a href="https://youtu.be/7gJDwNgQ6NU" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/195988535-a133a9b9-d094-45ba-ba64-c994b9a1e0ef.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;ColorMaster 脚本编写</a><br>
</details>
<details><summary>链接和块引用</summary>
<a href="https://youtu.be/qiKuqMcNWgU" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/171635214-30533c45-94fa-436e-83a9-b2ec99f190e2.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;链接视觉思维的 6 种策略 v4</a><br>
<a href="https://youtu.be/yZQoJg2RCKI" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/185791706-3d9983ab-7cb1-4b27-a016-30c039d84e34.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;图像的块引用部分</a><br>
<a href="https://youtu.be/Etskjw7a5zo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931461-0979b821-315a-41dd-86f1-31d169b7c127.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;链接到元素、垂直文本对齐、Markdown 样式</a><br>
<a href="https://youtu.be/2Y8OhkGiTHg" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/152585752-7eb0371f-0bab-40f6-a194-3b48e5811735.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Excalidraw 原生超链接使用指南</a><br>
</details>
<details><summary>强大工具</summary>
<a href="https://youtu.be/NOuddK6xrr8" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/147283367-e5689385-ea51-4983-81a3-04d810d39f62.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;便签(自动换行)</a><br>
<a href="https://youtu.be/eKFmrSQhFA4" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/149659524-2a4e0a24-40c9-4e66-a6b1-c92f3b88ecd5.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;本地字体</a><br>
<a href="https://youtu.be/vlC1-iBvIfo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/199207784-8bbe14e0-7d10-47d7-971d-20dce8dbd659.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;SVG 导入</a><br>
<a href="https://youtu.be/7gu4ETx7zro" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/202916770-28f2fa64-1ba2-4b40-a7fe-d721b42634f7.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;OCR</a><br>
<a href="https://youtu.be/U2LkBRBk4LY" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/159369910-6371f08d-b5fa-454d-9c6c-948f7e7a7d26.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;绑定/解绑文本与容器,前置标签自定义导出</a><br>
<a href="https://youtu.be/uZz5MgzWXiM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/211054371-8872e01a-77d6-4afc-a0c2-86a55410a8d3.png" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;自定义笔支持</a><br>
</details>
<details><summary>生活质量改善</summary>
<a href="https://youtu.be/qbPIAZguJeo" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/151705333-54e9ffd2-0bd7-4d02-b99e-0bd4e4708d4d.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;移动支持</a><br>
<a href="https://youtu.be/2v9TZmQNO8c" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/153676009-6f86b2d7-c248-49a2-b802-be21c6999e4f.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;托盘模式和可自定义调色板</a><br>
<a href="https://youtu.be/xHPGWR3m0c8" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/154821232-a404b6cf-72fb-4ce4-9d53-619132dce491.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;压缩 JSON 和改进的保存/同步支持</a><br>
<a href="https://youtu.be/gMIKXyhS-dM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/156931428-b2269fd9-87bd-43ab-8558-5572f40dff93.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;Obsidian 工具面板</a><br>
<a href="https://youtu.be/4N6efq1DtH0" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/158008902-12c6a851-237e-4edd-a631-d48e81c904b2.jpg" width="100" style="vertical-align: middle;"/>&nbsp;&nbsp;橡皮擦、左利手模式、改进的文件名配置</a><br>
</details>
### Beta 测试
该插件遵循每月发布的计划。如果您希望获得更频繁的更新包括新功能例如excalidraw.com 上的新内容,但尚未在 Obsidian 中提供)和小的 bug 修复,请加入 beta 社区。
[![缩略图 - 20240803 Excalidraw 发布方法(自定义)](https://github.com/user-attachments/assets/ab40648c-f73f-4bda-a416-52839f918f2a)](https://youtu.be/2poSS-Z91lY)
[![Excalidraw 插件发布策略(手机)](https://github.com/user-attachments/assets/87f1f379-782c-4c32-8b5b-d27fe2d3ac4b)](https://github.com/user-attachments/assets/120a0790-7239-48ae-bfbd-eb249f8b518d)
---
## 功能
- 该插件将 Excalidraw 无缝集成到 Obsidian 中,包括命令面板操作、文件资源管理器功能、选项菜单命令和工具栏按钮。
- 在工具栏按钮或文件资源管理器中 <kbd>CTRL/CMD+鼠标左键</kbd> 以在新面板中创建/打开绘图。
### 设置
设置将允许您根据需要自定义 Excalidraw。该插件提供了大量设置。我尝试为这些设置添加有意义的解释所以请耐心查找对于大多数请求已经存在相关设置。
插件设置分为以下几个部分:
- **基本设置**:例如使用的默认文件夹。
- **保存**:压缩和自动保存间隔。
- **文件名**:配置自动生成的 Excalidraw 文件名。
- **显示**:影响 Excalidraw 处理的设置(例如:左利手模式、主题设置、鼠标滚轮和捏合缩放设置、适应缩放设置)。
- **链接和嵌入**:影响链接和嵌入项在 Excalidraw 画布上行为的设置。
- **Markdown 嵌入设置**:这些设置控制从您的 Vault 嵌入到 Excalidraw 绘图中的 Markdown 文档的行为。
- **嵌入与导出**:控制 Excalidraw 图像在嵌入到 Markdown 文档时的显示方式的设置。
- **自动导出设置**:您可以配置 Excalidraw 在每次保存时创建绘图的 PNG 或 SVG 副本。
- **兼容性功能**:如果您在 Obsidian 之外编辑 Excalidraw 绘图(例如在 LogSeq、Visual Studio、网页等请检查这些设置。
- **实验性功能**:有一些高级功能作为“巧妙”的 hacks 实现,包括定义本地字体、添加自定义图标以区分 Obsidian 文件资源管理器中的 Excalidraw 文件、OCR 设置等。
- **已安装脚本的设置**:从脚本库安装的一些脚本附带设置。脚本设置在您第一次运行脚本时安装。因此,要访问脚本的设置,请安装脚本,首次运行后在插件设置中查找设置。
#### 模板
- 新绘图的模板。该模板将恢复笔画属性。这意味着您可以在模板中设置笔画颜色、笔画宽度、不透明度、字体系列、字体大小、填充样式、笔画样式等的默认值。这同样适用于 ExcalidrawAutomate。
- 通过模板,您可以自定义 Excalidraw 使用的调色板。
- 切换到 Markdown 视图。
- 滚动到文件底部,找到 `"AppState": {`
- 在 AppState 部分末尾找到 `"customColorPalette": {`
- 您可以通过添加以下三个变量中的任何一个或全部来指定 Excalidraw 使用的 3 个调色板:
- `"canvasBackground":[], "elementBackground":[], "elementStroke": []`
- 在每个变量的数组中添加有效 HTML 颜色的逗号分隔列表(例如,`#FF0000` 表示红色)。
- 有关更多帮助,请查看我上面的录像。
#### 导出
- 如果便携性对您很重要:
- 自动导出 SVG 和/或 PNG 文件,包括同步保持功能,这样您可以将 SVG/PNG 嵌入到文档中,而不是嵌入 Excalidraw 文件。
- 您可以通过添加 `excalidraw-autoexport` 前置字段键来覆盖单个文件的导出设置。该键的有效值为 `none``both``png``svg`
- 指定嵌入绘图的默认宽度。
- 兼容性功能以自动导出和保持同步 Markdown Excalidraw 文件及旧版 `.excalidraw` 文件。
- 实验性功能可在文件资源管理器中添加自定义标签以标记绘图文件。
- 启用/禁用自动保存。
### 将您的绘图嵌入到 Markdown 文档中
- 您可以使用以下格式自定义嵌入图像的大小和位置:
- `![[image.excalidraw|100]]`
- `![[image.excalidraw|100x100]]`
- `![[image.excalidraw|100|left]]`
- `![[image.excalidraw|right-wrap]]`
- `![[<filename.excalidraw>|<width>x<height>|<alignment>]]`
- 您可以通过 CSS 添加自定义 [对齐方式](https://www.scaler.com/topics/align-image-in-html/)。
- 出现在 `<alignment>` 中的任何文本将被添加到渲染的 SVG 元素样式和包装 DIV 元素中。
- 有关更多信息,请参见 [styles.css](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/styles.css)。
- Excalidraw 绘图在 Obsidian Publish 中不显示。如果您希望在发布的文档中使用 Excalidraw可以在插件设置中的 `Embed & Export` 下进行配置,以便在创建新文件时自动将绘图的 PNG 或 SVG 版本插入到文档中。请参见 `type of file to insert into document`
-`Export settings` 下,您还可以配置自动导出图像的深色和浅色版本,以便您的发布网站支持深色和浅色模式。
### 超链接和拖放支持
![](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/images/excalidraw-modifiers.png)
#### 超链接
- 支持超链接,例如:
- `https://zsolt.blog`
- `[Obsidian](https://obsidian.md)`,以及
- 内部链接,例如在绘图文本中使用 `[[My file in vault|Alias]]`
- 如果您启用了 Obsidian 设置中的“文件与链接/自动更新内部链接”,则文件移动或重命名时链接会自动更新。
- 绘图中的链接会出现在文档的反向链接中。
- 支持嵌入:
- `![[myfile#^blockref]]` 将绘图转换为该块的嵌入文本。
- `![[myfile#section]]` 也有效,这将嵌入该部分。
- 您还可以通过在嵌入后加上最大字符数的花括号来指定嵌入文本的换行,例如 `![[myfile#^blockref]]{40}` 将在 40 个字符处换行。
- 为了方便,您还可以使用命令面板将链接插入到绘图中。
- <kbd>CTRL/CMD + 鼠标悬停</kbd> 可以调出链接的 Obsidian 快速预览。(在 Mac 上为 <kbd>CTRL+CMD+鼠标悬停</kbd>)。
- 使用块引用,您还可以在其他文档中引用和嵌入绘图中出现的文本。
#### 拖放支持
- 您可以从 Obsidian 文件资源管理器中拖动文件,它们将成为 Excalidraw 中指向这些文件的链接。有关各种修饰键组合,请参见上面的表格。
- 注意:将图像锚定到其 100% 尺寸是一个非常小众的功能,具有非常特定的行为,我主要是为自己开发的。
- (甚至 Excalidraw Obsidian 中的其他功能更是如此 - 也是主要为自己开发的 😉)。
- 每次打开 Excalidraw 绘图时,这将重置您嵌入的图像为 100% 尺寸,或者如果您在画布上嵌入了使用此功能插入的 Excalidraw 绘图,每次更新嵌入的绘图时,它将缩放回 100% 尺寸。
- 这意味着即使您在绘图中调整了图像的大小,下次打开文件或修改原始嵌入对象时,它也会重置为 100%。此功能在将绘图分解为单独的 Excalidraw 文件时非常有用,但当它们组合到单个画布上时,您希望各个部分保持其实际大小。我使用此功能从原子绘图构建“一页书”摘要。
- 您可以将文本从 Markdown 视图拖放到 Excalidraw 中。
- 您可以从浏览器中拖放网页地址,它们将成为链接。
- 您可以拖放 YouTube 链接和缩略图,它们将在 Excalidraw 中成为带缩略图的 YouTube 链接。
### LaTeX
使用命令面板操作“插入 LaTeX 公式”插入 LaTeX 公式。您可以在 Markdown 视图中编辑公式,或者通过 <kbd>CTRL/CMD + 鼠标左键</kbd> 点击公式进行编辑。
### 图像支持
- 在 iOS 和 Android 上,您可以通过按下 Excalidraw 中的添加图像按钮从相机添加图像。
- 您可以将图像复制/粘贴到绘图中。图像将保存在您的 Vault 中。
- 您可以按照上面的说明拖放图像。
- URL 链接到网络上的图像:您可以从网页将图像拖放到 Excalidraw。如果在将图像拖放到 Excalidraw 时按住 CTRL 键,图像将不会保存到您的 Vault 中。Excalidraw 将从 URL 加载图像。请注意,如果您没有互联网连接,或者这些图像从互联网上被删除,它们也会从您的绘图中消失。
- 如果您将图像 URL 粘贴到 Excalidraw只需点击 URL 复制,然后在 Excalidraw 画布上点击粘贴),图像将以链接形式插入到网络图像上。同样,图像不会保存到您的 Vault 中,只有链接会被保存。
- 如果您拖放 YouTube 视频链接,它将转换为一个缩略图,并带有指向视频的元素链接。
### 引用图像部分的块
有关更多详细信息,请参见此 [视频](https://youtu.be/yZQoJg2RCKI)。
- 当通过链接引用 Excalidraw 文件中的画布元素时,可以使用:
- 元素 ID 或章节标题(即包含 `# <章节标题>` 的文本元素)
- 例如 `[[file#^elementID]]`
- 您可以添加 `group=` 前缀,
- 例如 `[[file#^group=elementID]]`,或
- `area=` 前缀,
- 例如 `[[file#area=Section heading]]`
- 如果找到 `group=` 前缀Excalidraw 将选择与通过元素 ID块引用或章节标题引用的元素在同一组中的元素。
- 如果找到 `area=` 前缀Excalidraw 将在引用的元素周围插入图像的剪切部分。
- 请注意,当将 Excalidraw 嵌入为 PNG 到您的 Markdown 文档时,不支持 `area=` 选择器。
- 引用文本元素的元素 ID 而不带 `group=``area=` 前缀将以普通文本嵌入该元素。引用非文本元素(例如矩形、椭圆等)而不带 `group=``area=` 前缀将导致 Obsidian 错误,因为这些元素 ID 在 Excalidraw Markdown 文件中不能够作为块引用。
### Markdown
- 从 1.2.0 版本开始,绘图文件存储在 Markdown 文件中。
- 您可以为绘图添加标签。
- 您可以在绘图的 YAML 前置字段中添加元数据。
- 您在前置字段和 `# Text Elements` 标题之间添加的任何内容将被 Excalidraw 忽略,即您可以在这里添加任何内容,它将作为文档的一部分被保留。
- Excalidraw 文档现在会在图形视图中显示。
- 以下前置字段键将自定义绘图的显示方式 - 覆盖一般设置:
- `excalidraw-link-prefix: "📍"` 内部链接的预览前缀
- `excalidraw-url-prefix: "🌐"` 外部链接的预览前缀
- `excalidraw-link-brackets: true|false` 是否在预览中显示链接周围的括号
- `excalidraw-default-mode: view|zen` 默认以查看模式或禅模式打开此文档。默认查看模式非常适合演示幻灯片。
- 前置字段标签用于在文件级别自定义图像导出 [519](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/519)。如果这些键存在,它们将覆盖默认的 Excalidraw 嵌入和导出设置。
- `excalidraw-export-transparent: true` true == 透明 / false == 有背景。
- `excalidraw-export-dark` true == 深色模式 / false == 浅色模式。
- `excalidraw-export-padding`:指定图像的导出边距。
- `excalidraw-export-pngscale`:这仅影响导出为 PNG。指定图像的导出比例。典型范围在 0.5 到 5 之间,但您也可以尝试其他值。
### 将完整的 Markdown 文件嵌入到您的绘图中
从 Obsidian 文件资源管理器中拖动所需文件,同时按住 <kbd>SHIFT</kbd> 将文件放到画布上。
- 使用命令面板操作:`从 Vault 插入 Markdown 文件`
- 使用自定义的 woff、woff2 或 TTF 字体来显示文档,您可以在 Excalidraw 设置中设置默认字体。
- 您可以为渲染 Markdown 文档的快照图像设置自定义 CSS。仅支持操作系统标准字体作为字体系列[Win10](https://docs.microsoft.com/en-us/typography/fonts/windows_10_font_list)、[Mac & iOS](https://developer.apple.com/fonts/system-fonts/)),此外,您可以使用上述设置添加一个额外的自定义字体。
- (要查看演示,请观看此 [视频](https://youtu.be/K6qZkTz8GHs) 并查看此
- [示例 CSS](https://github.com/zsviczian/obsidian-excalidraw-plugin/discussions/281))。
- 为了帮助样式设置,您可以查看 Excalidraw 创建的 Markdown 文档的 SVG 快照。
- 打开 Obsidian 开发者控制台 (<kbd>CTRL+Shift+i</kbd>/<kbd>CMD+OPT+i</kbd>),并
- 执行以下命令:`ExcalidrawAutomate.mostRecentMarkdownSVG`
- 您可以通过将以下前置字段键添加到您的 Markdown 文档,按文件控制嵌入 Markdown 文件的外观:
- `excalidraw-font: Virgil|Cascadia|font_file_name.extension`
- `excalidraw-font-color: css-color-name|#HEXcolor|any-other-html-standard-format`
- 您可以在 [这里](https://www.w3schools.com/colors/colors_names.asp) 找到 CSS 颜色名称。
- `excalidraw-border-color: css-color-name|#HEXcolor|any-other-html-standard-format`
- `excalidraw-css: "css-filename|css snippet"`
- 切换到 Markdown 视图或使用 <kbd>WIN+CTRL</kbd>/<kbd>CMD+CTRL</kbd> 点击图像以编辑嵌入的属性:
- `[[filename#^blockref|WIDTHxMAXHEIGHT]]`
### 自定义字体、自定义笔、OCR 支持、SVG 导入
- 在插件设置中,您可以添加自定义的本地字体。有关更多详细信息,请参见此 [视频](https://youtu.be/eKFmrSQhFA4)。
- 该插件包括使用 Taskbone OCR 的 OCR 支持。有关更多详细信息,请参见此 [视频](https://youtu.be/7gu4ETx7zro)。
- 您可以将 SVG 文件转换为 Excalidraw 绘图(有一些限制)。有关更多详细信息,请参见此 [视频](https://youtu.be/vlC1-iBvIfo)。
- 您可以定义自定义笔和荧光笔,并将其固定到侧边栏。有关更多详细信息,请参见此 [视频](https://youtu.be/OjNhjaH2KjI)。使用 ExcalidrawAutomate您可以添加对 [自动切换](<ea-scripts/Auto Draw for Pen.md>) 笔的支持,以及对 [硬件橡皮擦按钮](<ea-scripts/Hardware Eraser Support.md>) 的支持。
### 脚本引擎
- 从 1.5.0 版本开始,您可以轻松执行 ExcalidrawAutomate 宏,并为它们分配命令面板快捷键,使用脚本引擎。您可以在 [这里](ea-scripts/README.md) 找到介绍视频和不断增加的可安装脚本库。
- 您可以通过将脚本和随附的 SVG 图标文件移动到文件夹中,将脚本组织成组,放在 Excalidraw 的 Obsidian 工具面板中。请参见演示 [视频](https://youtu.be/wTtaXmRJ7wg?t=16)。
### 其他
- 左利手模式
- 包含完整的
- [QuickAdd](https://github.com/chhoumann/quickadd)
- [Templater](https://silentvoid13.github.io/Templater/) 和
- [Dataview](https://blacksmithgu.github.io/obsidian-dataview/docs/api/intro/) 支持,通过 ExcalidrawAutomate 实现。
- 查看 [详细帮助 + 示例](https://zsviczian.github.io/obsidian-excalidraw-plugin/)。
- 我还有一个 [YouTube ExcalidrawAutomate 播放列表](https://www.youtube.com/playlist?list=PL6mqgtMZ4NP1IR4nXxSlMA4PA5E-qpyHZ),里面有很多示例。
- 需要 Obsidian Sync 订阅:完整的绘图文件历史记录和设备之间的同步。
- 多语言支持:如果您想通过翻译插件来帮助,请与我联系。
## 反馈、问题、想法、问题
请在 [forum.obsidian.md](https://forum.obsidian.md/t/excalidraw-full-featured-sketching-plugin-in-obsidian) 上参与关于 Excalidraw 插件的讨论。
请前往 [GitHub](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues) 报告错误或请求增强功能。
---
## 感谢支持
如果您喜欢 Excalidraw请通过在 [https://ko-fi/zsolt](https://ko-fi.com/zsolt) 上请我喝杯咖啡来支持我的工作和热情。
请通过在 Twitter、Reddit 或其他您常用的社交媒体平台上分享 Obsidian Excalidraw 插件来帮助传播消息。
您可以在 Twitter 上找到我 [@zsviczian](https://twitter.com/zsviczian),以及我的博客 [zsolt.blog](https://zsolt.blog)。
[<img style="float:left" src="https://user-images.githubusercontent.com/14358394/115450238-f39e8100-a21b-11eb-89d0-fa4b82cdbce8.png" width="200">](https://ko-fi.com/zsolt)
---
## Excalidraw 的朋友们
如果您喜欢 Excalidraw可以考虑尝试 [ExcaliBrain](https://github.com/zsviczian/excalibrain)(也可通过 Obsidian 社区插件获得)。
<a href="https://youtu.be/gOkniMkDPyM" target="_blank"><img src="https://user-images.githubusercontent.com/14358394/169708346-9e41289d-9536-43ec-8f70-2d2ad2d369d6.png" width="300"/></a>

View File

Before

Width:  |  Height:  |  Size: 632 B

After

Width:  |  Height:  |  Size: 632 B

View File

@@ -4,7 +4,7 @@
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
1) send the selected image file to [taskbone.com](https://taskbone.com) to extract 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.

View File

@@ -15,7 +15,7 @@ In the `Command Palette` installed scripts are prefixed with `Downloaded/`, thus
## 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).
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 available (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.
@@ -243,7 +243,7 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/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>
<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 extract 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>
## Organic Line
```excalidraw-script-install

View File

@@ -1,67 +0,0 @@
/*
Automatically switches between the select and draw tools, based on whether a pen is being used.
1. Choose the select tool
2. Hover/use the pen to draw, move it away to return to select mode
*This is based on pen hover status, so will only work if your pen supports hover!*
If you click draw with the mouse or press select with the pen, switching will be disabled until the opposite input method is used.
**Note:** This script will stay active until the *Obsidian* window is closed.
Compatible with my *Hardware Eraser Support* script
```javascript
*/
(function() {
'use strict';
let promise
let timeout
let disable
function handlePointer(e) {
ea.setView("active");
var activeTool = ea.getExcalidrawAPI().getAppState().activeTool;
function setActiveTool(t) {
ea.getExcalidrawAPI().setActiveTool(t)
}
if (e.pointerType === 'pen') {
if (disable) return
if (!promise && activeTool.type==='selection') {
setActiveTool({type:"freedraw"})
}
if (timeout) clearTimeout(timeout)
function setTimeoutX(a,b) {
timeout = setTimeout(a,b)
return timeout
}
function revert() {
activeTool = ea.getExcalidrawAPI().getAppState().activeTool;
disable = false
if (activeTool.type==='freedraw') {
setActiveTool({type:"selection"})
} else if (activeTool.type==='selection') {
disable = true
}
promise = false
}
promise = new Promise(resolve => setTimeoutX(resolve, 500))
promise.then(() => revert())
}
}
function handleClick(e) {
ea.setView("active");
if (e.pointerType !== 'pen') {
disable = false
}
}
window.addEventListener('pointermove', handlePointer, { capture: true })
window.addEventListener('pointerdown', handleClick, { capture: true })
})();

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 448 512" style="enable-background:new 0 0 448 512;" xml:space="preserve">
<style type="text/css">
.st0{stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
</style>
<g>
<g>
<path class="st0" d="M355.8,234.1"/>
</g>
<g>
<path d="M32.3,139.7l28.8,24.2l63.5-71.7L95.7,67c-7.2-6.3-18.2-5.6-24.5,1.6l-40.6,46.6C24.3,122.4,25,133.3,32.3,139.7z"/>
<path d="M61.2,165.3l-29.6-24.9c-3.7-3.3-5.9-7.8-6.3-12.7c-0.3-4.9,1.3-9.6,4.5-13.2L70.5,68c6.7-7.6,18.3-8.4,25.9-1.7L126,92.1
L61.2,165.3z M32.9,138.9l28,23.6l62.2-70.2l-28-24.6c-6.8-5.9-17.1-5.2-23.1,1.5l-40.6,46.6c-2.9,3.3-4.3,7.5-4,11.8
C27.6,132,29.6,136,32.9,138.9z"/>
</g>
<g>
<polygon points="218.7,240.1 212.3,168.6 197.2,155.4 133.7,228.1 148.9,241.3 "/>
<path d="M148.5,242.3l-16.2-14.1l64.8-74.2l16.2,14.1l6.5,73L148.5,242.3z M135.1,228l14.1,12.3l68.4-1.2l-6.2-70.1l-14.1-12.3
L135.1,228z"/>
</g>
<g>
<polygon points="192.6,151.6 129.1,224.3 66.2,168.4 129.6,96.7 "/>
<path d="M129.2,225.7l-64.5-57.2l64.8-73.2l64.5,56.2L129.2,225.7z M67.6,168.3l61.5,54.6l62.2-71.2l-61.5-53.6L67.6,168.3z"/>
</g>
<g>
<path d="M109.7,381.6c-23.7-22.2-40-49.3-48.9-78.2c8.2-0.9,22.4-3.6,30.1-12.3c-12.6-12.5-25.3-25-37.9-37.5c0-0.1,0-0.3,0-0.4
l-23.6-22c-6,60.7,15.5,123.7,63.7,168.8s112.5,62.4,172.7,52.4l-24.1-22.6C194.8,432.3,146.9,416.4,109.7,381.6z"/>
<path d="M232.6,456.1c-19.6,0-39.2-2.8-57.9-8.3c-30.9-9.1-58.6-24.9-82.3-47.1C68.7,378.6,51.1,352,40,321.8
c-10.6-28.8-14.6-60.2-11.6-90.7l0.2-2L54,252.8v0.4c6.2,6.1,12.4,12.3,18.6,18.4c6.3,6.3,12.7,12.5,19,18.8l0.7,0.7l-0.6,0.7
c-7.2,8.1-19.8,11.3-29.5,12.5c9.2,29.2,25.9,55.6,48.3,76.6l0,0c35.8,33.5,82.4,50.5,131.3,47.9l0.4,0l25.9,24.3l-2,0.3
C255,455.2,243.8,456.1,232.6,456.1z M30.2,233.3c-5.6,62.5,17.5,122.9,63.6,166c46,43.1,107.8,62.1,169.8,52.5l-22.3-20.9
c-49.2,2.5-96.2-14.7-132.3-48.5l0,0c-22.9-21.5-39.9-48.7-49.2-78.6l-0.4-1.2l1.2-0.1c9.3-1,21.6-3.8,28.8-11.3
c-6.1-6-12.2-12.1-18.3-18.1c-6.3-6.2-12.6-12.5-18.9-18.7L52,254v-0.4L30.2,233.3z"/>
</g>
<g>
<path d="M368.8,105.4c-56-52.4-133.5-67.2-201.3-45.5l21,19.6c56.1-13.2,117.7,1.1,163.1,43.7c33,30.9,51.7,71.2,55.9,112.7
c-0.2-0.4-0.3-0.6-0.3-0.6s-25.1-0.1-36.5,12.7c11.8,11.6,23.5,23.3,35.3,34.9v0.1l2.4,2.3c0.4,0.4,0.9,0.9,1.3,1.3l0,0l17.7,16.6
C444.7,234.1,424.8,157.7,368.8,105.4z"/>
<path d="M428,305.1L409,287.3l-1.3-1.3l-2.7-2.6v-0.1c-5.8-5.7-11.5-11.4-17.3-17.1c-5.9-5.8-11.8-11.7-17.7-17.5l-0.7-0.7
l0.6-0.7c10.5-11.8,31.7-12.9,36.4-13c-4.7-42.1-24.3-81.3-55.4-110.4c-43.5-40.9-104.2-57.1-162.2-43.5l-0.5,0.1l-22.6-21.1
l1.6-0.5c70.5-22.6,148-5,202.3,45.7c54.3,50.7,76.9,126.9,58.9,198.8L428,305.1z M407,282.6l3.4,3.3l16.4,15.4
c17.1-70.7-5.3-145.3-58.7-195.2l0,0c-53.3-49.9-129.3-67.4-198.7-45.8l19.4,18.1c58.5-13.6,119.6,2.9,163.5,44.1
c31.9,29.8,51.8,70.1,56.2,113.3l0.5,5.4l-2.5-4.9c-3.8,0.1-24.3,1.1-34.5,11.7c5.7,5.6,11.3,11.2,17,16.8
c5.9,5.8,11.7,11.6,17.6,17.4L407,282.6L407,282.6z"/>
</g>
<polygon points="425.2,382.2 302.7,283.9 299.4,437.6 340.9,383.8 382.3,456.5 398,447.5 359.4,379.8 "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,369 @@
/*
With This Script it is possible to make boolean Operations on Shapes.
The style of the resulting shape will be the style of the highest ranking Element that was used.
The ranking of the elements is based on their background. The "denser" the background, the higher the ranking (the order of backgroundstyles is shown below). If they have the same background the opacity will decide. If thats also the same its decided by the order they were created.
The ranking is also important for the difference operation, so a transparent object for example will cut a hole into a solid object.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-boolean-operations-showcase.png)
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-boolean-operations-element-ranking.png)
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.9.20")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
const ShadowGroupMarker = "ShadowCloneOf-";
const elements = ea.getViewSelectedElements().filter(
el=>["ellipse", "rectangle", "diamond"].includes(el.type) ||
el.groupIds.some(id => id.startsWith(ShadowGroupMarker)) ||
(["line", "arrow"].includes(el.type) && el.roundness === null)
);
if(elements.length === 0) {
new Notice ("Select ellipses, rectangles or diamonds");
return;
}
const PolyBool = ea.getPolyBool();
const polyboolAction = await utils.suggester(["union (a + b)", "intersect (a && b)", "difference (a - b)", "reversed difference (b - a)", "xor"], [
PolyBool.union, PolyBool.intersect, PolyBool.difference, PolyBool.differenceRev, PolyBool.xor
], "What would you like todo with the object");
const shadowClones = elements.filter(element => element.groupIds.some(id => id.startsWith(ShadowGroupMarker)));
shadowClones.forEach(shadowClone => {
let parentId = shadowClone.groupIds
.filter(id => id.startsWith(ShadowGroupMarker))[0]
.slice(ShadowGroupMarker.length);
const shadowCloneIndex = elements.findIndex(element => element.id == parentId);
if (shadowCloneIndex == -1) return;
elements[shadowCloneIndex].backgroundColor = shadowClone.backgroundColor;
elements[shadowCloneIndex].fillStyle = shadowClone.fillStyle;
})
const borderElements = elements.filter(element => !element.groupIds.some(id => id.startsWith(ShadowGroupMarker)));
groups = ea.getMaximumGroups(borderElements);
groups = groups.map((group) => group.sort((a, b) => RankElement(b) - RankElement(a)));
groups.sort((a, b) => RankElement(b[0]) - RankElement(a[0]));
ea.style.strokeColor = groups[0][0].strokeColor;
ea.style.backgroundColor = groups[0][0].backgroundColor;
ea.style.fillStyle = groups[0][0].fillStyle;
ea.style.strokeWidth = groups[0][0].strokeWidth;
ea.style.strokeStyle = groups[0][0].strokeStyle;
ea.style.roughness = groups[0][0].roughness;
ea.style.opacity = groups[0][0].opacity;
const basePolygons = groups.shift().map(element => traceElement(element));
const toolPolygons = groups.flatMap(group => group.map(element => traceElement(element)));
const result = polyboolAction({
regions: basePolygons,
inverted: false
}, {
regions: toolPolygons,
inverted: false
});
const polygonHierachy = subordinateInnerPolygons(result.regions);
drawPolygonHierachy(polygonHierachy);
ea.deleteViewElements(elements);
ea.addElementsToView(false,false,true);
return;
function traceElement(element) {
const diamondPath = (diamond) => [
SxVEC(1/2, [0, diamond.height]),
SxVEC(1/2, [diamond.width, 0]),
addVec([SxVEC(1/2, [0, diamond.height]), ([diamond.width, 0])]),
addVec([SxVEC(1/2, [diamond.width, 0]), ([0, diamond.height])]),
SxVEC(1/2, [0, diamond.height])
];
const rectanglePath = (rectangle) => [
[0,0],
[0, rectangle.height],
[rectangle.width, rectangle.height],
[rectangle.width, 0],
[0, 0]
]
const ellipsePath = (ellipse) => {
const angle = ellipse.angle;
const width = ellipse.width;
const height = ellipse.height;
const ellipseAtPoint = (t) => {
const spanningVector = [width/2*Math.cos(t), height/2*Math.sin(t)];
const baseVector = [width/2, height/2];
return addVec([spanningVector, baseVector]);
}
let points = [];
step = (2*Math.PI)/64
for (let t = 0; t < 2*Math.PI; t = t + step) {
points.push(ellipseAtPoint(t));
}
return points;
}
let polygon;
let correctForPolygon = [0, 0];
switch (element.type) {
case "diamond":
polygon = diamondPath(element);
break;
case "rectangle":
polygon = rectanglePath(element);
break;
case "ellipse":
polygon = ellipsePath(element);
break;
case "line":
case "arrow":
if (element.angle != 0) {
let smallestX = 0;
let smallestY = 0;
element.points.forEach(point => {
if (point[0] < smallestX) smallestX = point[0];
if (point[1] < smallestY) smallestY = point[1];
});
polygon = element.points.map(point => {
return [
point[0] -= smallestX,
point[1] -= smallestY
];
});
correctForPolygon = [smallestX, smallestY];
break;
}
if (element.roundness) {
new Notice("This script does not work with curved lines or arrows yet!");
return [];
}
polygon = element.points;
default:
break;
}
if (element.angle == 0) return polygon.map(v => addVec([v, [element.x, element.y]]));
polygon = polygon.map(v => addVec([v, SxVEC(-1/2, [element.width, element.height])]));
polygon = rotateVectorsByAngle(polygon, element.angle);
return polygon.map(v => addVec([v, [element.x, element.y], SxVEC(1/2, [element.width, element.height]), correctForPolygon]));
}
function RankElement(element) {
let score = 0;
const backgroundRank = [
"dashed",
"none",
"hachure",
"zigzag",
"zigzag-line",
"cross-hatch",
"solid"
]
score += (backgroundRank.findIndex((fillStyle) => fillStyle == element.fillStyle) + 1) * 10;
if (element.backgroundColor == "transparent") score -= 100;
if (element.points && getVectorLength(element.points[element.points.length - 1]) > 8) score -= 100;
if (score < 0) score = 0;
score += element.opacity / 100;
return score;
}
function drawPolygonHierachy(polygonHierachy) {
const backgroundColor = ea.style.backgroundColor;
const strokeColor = ea.style.strokeColor;
const setInnerStyle = () => {
ea.style.backgroundColor = backgroundColor;
ea.style.strokeColor = "transparent";
}
const setBorderStyle = () => {
ea.style.backgroundColor = "transparent";
ea.style.strokeColor = strokeColor;
}
const setFilledStyle = () => {
ea.style.backgroundColor = backgroundColor;
ea.style.strokeColor = strokeColor;
}
polygonHierachy.forEach(polygon => {
setFilledStyle();
let path = polygon.path;
path.push(polygon.path[0]);
if (polygon.innerPolygons.length === 0) {
ea.addLine(path);
return;
}
const outerBorder = path;
const innerPolygons = addInnerPolygons(polygon.innerPolygons);
path = path.concat(innerPolygons.backgroundPath);
path.push(polygon.path[0]);
setInnerStyle();
const backgroundId = ea.addLine(path);
setBorderStyle();
const outerBorderId = ea.addLine(outerBorder)
const innerBorderIds = innerPolygons.borderPaths.map(path => ea.addLine(path));
const allIds = [innerBorderIds, outerBorderId, backgroundId].flat();
ea.addToGroup(allIds);
const background = ea.getElement(backgroundId);
background.groupIds.push(ShadowGroupMarker + outerBorderId);
});
}
function addInnerPolygons(polygonHierachy) {
let firstPath = [];
let secondPath = [];
let borderPaths = [];
polygonHierachy.forEach(polygon => {
let path = polygon.path;
path.push(polygon.path[0]);
borderPaths.push(path);
firstPath = firstPath.concat(path);
secondPath.push(polygon.path[0]);
drawPolygonHierachy(polygon.innerPolygons);
});
return {
backgroundPath: firstPath.concat(secondPath.reverse()),
borderPaths: borderPaths
};
}
function subordinateInnerPolygons(polygons) {
const polygonObjectPrototype = (polygon) => {
return {
path: polygon,
innerPolygons: []
};
}
const insertPolygonIntoHierachy = (polygon, hierarchy) => {
for (let i = 0; i < hierarchy.length; i++) {
const polygonObject = hierarchy[i];
let inside = null;
let pointIndex = 0;
do {
inside = pointInPolygon(polygon[pointIndex], polygonObject.path);
pointIndex++
} while (inside === null);
if (inside) {
hierarchy[i].innerPolygons = insertPolygonIntoHierachy(polygon, hierarchy[i].innerPolygons);
return hierarchy;
}
}
polygon = polygonObjectPrototype(polygon);
for (let i = 0; i < hierarchy.length; i++) {
const polygonObject = hierarchy[i];
let inside = null;
let pointIndex = 0;
do {
inside = pointInPolygon(polygonObject.path[pointIndex], polygon.path);
pointIndex++
} while (inside === null);
if (inside) {
polygon.innerPolygons.push(hierarchy.splice(i, 1)[0]);
i--;
}
}
hierarchy.push(polygon);
return hierarchy;
}
let polygonHierachy = [];
polygons.forEach(polygon => {
polygonHierachy = insertPolygonIntoHierachy(polygon, polygonHierachy);
})
return polygonHierachy;
}
/**
* Checks if the given point lays in the polygon
* @param point array [x, y]
* @param polygon array [[x, y], ...]
* @returns true if inside, false if not, null if the point is on one of the polygons vertecies
*/
function pointInPolygon(point, polygon) {
const x = point[0];
const y = point[1];
let inside = false;
// odd even test if point is in polygon
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
const xi = polygon[i][0];
const yi = polygon[i][1];
const xj = polygon[j][0];
const yj = polygon[j][1];
const intersect =
yi > y !== yj > y &&
x < ((xj - xi) * (y - yi)) / (yj - yi) + xi;
if (intersect) {
inside = !inside;
}
if ((x === xi && y === yi) || (x === xj && y === yj)) {
return null;
}
}
return inside;
}
function getVectorLength(vector) {
return Math.sqrt(vector[0]**2+vector[1]**2);
}
/**
* Adds two Vectors together
*/
function addVec(vectors) {
return vectors.reduce((acc, vec) => [acc[0] + vec[0], acc[1] + vec[1]], [0, 0]);
}
/**
* Returns the negative of the vector
*/
function negVec(vector) {
return [-vector[0], -vector[1]];
}
/**
* Multiplies Vector with a scalar
*/
function SxVEC(scalar, vector) {
return [vector[0] * scalar, vector[1] * scalar];
}
function rotateVector (vec, ang) {
var cos = Math.cos(ang);
var sin = Math.sin(ang);
return [vec[0] * cos - vec[1] * sin, vec[0] * sin + vec[1] * cos];
}
function rotateVectorsByAngle(vectors, angle) {
const cosAngle = Math.cos(angle);
const sinAngle = Math.sin(angle);
const rotationMatrix = [
[cosAngle, -sinAngle],
[sinAngle, cosAngle]
];
return applyTranformationMatrix(vectors, rotationMatrix);
}
function applyTranformationMatrix(vectors, transformationMatrix) {
const result = [];
for (const vector of vectors) {
const x = vector[0];
const y = vector[1];
const newX = transformationMatrix[0][0] * x + transformationMatrix[0][1] * y;
const newY = transformationMatrix[1][0] * x + transformationMatrix[1][1] * y;
result.push([newX, newY]);
}
return result;
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -0,0 +1,102 @@
/*
Connects two lines. Lines may be type of arrow or line. The resulting line will carry the style of the line higher in the drawing layers (bring to front the one you want to control the look and feel). Arrows are connected intelligently.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-concatenate-lines.png)
```js*/
const lines = ea.getViewSelectedElements().filter(el=>el.type==="line" || el.type==="arrow");
if(lines.length !== 2) {
new Notice ("Select two lines or arrows");
return;
}
// https://math.stackexchange.com/questions/2204520/how-do-i-rotate-a-line-segment-in-a-specific-point-on-the-line
const rotate = (point, element) => {
const [x1, y1] = point;
const x2 = element.x + element.width/2;
const y2 = element.y - element.height/2;
const angle = element.angle;
return [
(x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle) + x2,
(x1 - x2) * Math.sin(angle) + (y1 - y2) * Math.cos(angle) + y2,
];
}
const points = lines.map(
el=>el.points.map(p=>rotate([p[0]+el.x, p[1]+el.y],el))
);
const last = (p) => p[p.length-1];
const first = (p) => p[0];
const distance = (p1,p2) => Math.sqrt((p1[0]-p2[0])**2+(p1[1]-p2[1])**2);
const distances = [
distance(first(points[0]),first(points[1])),
distance(first(points[0]),last (points[1])),
distance(last (points[0]),first(points[1])),
distance(last (points[0]),last (points[1]))
];
const connectDirection = distances.indexOf(Math.min(...distances));
let newPoints = [];
switch(connectDirection) {
case 0: //first-first
newPoints = [...points[0].reverse(),...points[1].slice(1)];
break;
case 1: //first-last
newPoints = [...points[0].reverse(),...points[1].reverse().slice(1)];
break;
case 2: //last-first
newPoints = [...points[0],...points[1].slice(1)];
break;
case 3: //last-last
newPoints = [...points[0],...points[1].reverse().slice(1)];
break;
}
["strokeColor", "backgrounColor", "fillStyle", "roundness", "roughness", "strokeWidth", "strokeStyle", "opacity"].forEach(prop=>{
ea.style[prop] = lines[1][prop];
})
ea.style.startArrowHead = null;
ea.style.endArrowHead = null;
ea.copyViewElementsToEAforEditing(lines);
ea.getElements().forEach(el=>{el.isDeleted = true});
const lineTypes = parseInt(lines.map(line => line.type === "line" ? '1' : '0').join(''),2);
switch (lineTypes) {
case 0: //arrow - arrow
ea.addArrow(
newPoints,
connectDirection === 0 //first-first
? { startArrowHead: lines[0].endArrowhead, endArrowHead: lines[1].endArrowhead }
: connectDirection === 1 //first-last
? { startArrowHead: lines[0].endArrowhead, endArrowHead: lines[1].startArrowhead }
: connectDirection === 2 //last-first
? { startArrowHead: lines[0].startArrowhead, endArrowHead: lines[1].endArrowhead }
//3: last-last
: { startArrowHead: lines[0].startArrowhead, endArrowHead: lines[1].startArrowhead }
);
break;
case 1: //arrow - line
reverse = connectDirection === 0 || connectDirection === 1;
ea.addArrow(newPoints,{
startArrowHead: reverse ? lines[0].endArrowhead : lines[0].startArrowhead,
endArrowHead: reverse ? lines[0].startArrowhead : lines[0].endArrowhead
});
break;
case 2: //line - arrow
reverse = connectDirection === 1 || connectDirection === 3;
ea.addArrow(newPoints,{
startArrowHead: reverse ? lines[1].endArrowhead : lines[1].startArrowhead,
endArrowHead: reverse ? lines[1].startArrowhead : lines[1].endArrowhead
});
break;
case 3: //line - line
ea.addLine(newPoints);
break;
}
ea.addElementsToView();

View File

@@ -0,0 +1,17 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72.75819749055177 80.03703336574608" width="72.75819749055177" height="80.03703336574608">
<!-- svg-source:excalidraw -->
<defs>
<style class="style-fonts">
@font-face {
font-family: "Virgil";
src: url("https://excalidraw.com/Virgil.woff2");
}
@font-face {
font-family: "Cascadia";
src: url("https://excalidraw.com/Cascadia.woff2");
}
</style>
</defs>
<g stroke-linecap="round"><g transform="translate(4 4) rotate(0 12.71901889991409 17.183109917454658)"><path d="M0 0 C0 7.02, 0 14.05, 0 34.37 M0 34.37 C7.62 34.37, 15.24 34.37, 25.44 34.37" stroke="black" stroke-width="4.5" fill="none" stroke-dasharray="1.5 10"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(51.379765518092086 61.93633577499986) rotate(0 5.684341886080802e-14 7.050348795373111)"><path d="M0 0 C0 4.06, 0 8.11, 0 14.1" stroke="black" stroke-width="4.5" fill="none" stroke-dasharray="1.5 10"></path></g></g><mask></mask><g stroke-linecap="round" transform="translate(34.0013341989918 20.987787610339183) rotate(0 17.378431645779926 17.378431645779983)"><path d="M34.76 17.38 C34.76 18.38, 34.67 19.41, 34.49 20.4 C34.32 21.39, 34.05 22.38, 33.71 23.32 C33.36 24.27, 32.93 25.2, 32.43 26.07 C31.93 26.94, 31.34 27.78, 30.69 28.55 C30.04 29.32, 29.32 30.04, 28.55 30.69 C27.78 31.34, 26.94 31.93, 26.07 32.43 C25.2 32.93, 24.27 33.36, 23.32 33.71 C22.38 34.05, 21.39 34.32, 20.4 34.49 C19.41 34.67, 18.38 34.76, 17.38 34.76 C16.37 34.76, 15.35 34.67, 14.36 34.49 C13.37 34.32, 12.38 34.05, 11.43 33.71 C10.49 33.36, 9.56 32.93, 8.69 32.43 C7.82 31.93, 6.98 31.34, 6.21 30.69 C5.44 30.04, 4.71 29.32, 4.07 28.55 C3.42 27.78, 2.83 26.94, 2.33 26.07 C1.83 25.2, 1.39 24.27, 1.05 23.32 C0.7 22.38, 0.44 21.39, 0.26 20.4 C0.09 19.41, 0 18.38, 0 17.38 C0 16.37, 0.09 15.35, 0.26 14.36 C0.44 13.37, 0.7 12.38, 1.05 11.43 C1.39 10.49, 1.83 9.56, 2.33 8.69 C2.83 7.82, 3.42 6.98, 4.07 6.21 C4.71 5.44, 5.44 4.71, 6.21 4.07 C6.98 3.42, 7.82 2.83, 8.69 2.33 C9.56 1.83, 10.49 1.39, 11.43 1.05 C12.38 0.7, 13.37 0.44, 14.36 0.26 C15.35 0.09, 16.37 0, 17.38 0 C18.38 0, 19.41 0.09, 20.4 0.26 C21.39 0.44, 22.38 0.7, 23.32 1.05 C24.27 1.39, 25.2 1.83, 26.07 2.33 C26.94 2.83, 27.78 3.42, 28.55 4.07 C29.32 4.71, 30.04 5.44, 30.69 6.21 C31.34 6.98, 31.93 7.82, 32.43 8.69 C32.93 9.56, 33.36 10.49, 33.71 11.43 C34.05 12.38, 34.32 13.37, 34.49 14.36 C34.67 15.35, 34.71 16.88, 34.76 17.38 C34.8 17.88, 34.8 16.88, 34.76 17.38" stroke="black" stroke-width="4" fill="none"></path></g><g stroke-linecap="round"><g transform="translate(41.72257566145686 38.36621939788711) rotate(0 9.65718949485347 0)"><path d="M0 0 C4.11 0, 8.22 0, 19.31 0 M0 0 C6.95 0, 13.9 0, 19.31 0" stroke="black" stroke-width="4" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(41.72257587602678 38.36622004108449) rotate(89.99999999999994 9.65718949485347 0)"><path d="M0 0 C5.31 0, 10.62 0, 19.31 0 M0 0 C4.56 0, 9.13 0, 19.31 0" stroke="black" stroke-width="4" fill="none"></path></g></g><mask></mask></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,75 @@
/*
Adds a rounded mask to the image by adding a full cover black mask and a rounded rectangle white mask. The script is also useful for adding just a black mask. In this case, run the script, then delete the white mask and add your custom white mask.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-crop-vintage.jpg)
```js*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.0.18")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
if(!ea.isExcalidrawMaskFile()) {
new Notice("This script only works with Mask Files");
return;
}
const frames = ea.getViewElements().filter(el=>el.type==="frame")
if(frames.length !== 1) {
new Notice("Multiple frames found");
return;
}
const frame = frames[0];
ea.copyViewElementsToEAforEditing(ea.getViewElements().filter(el=>el.frameId === frame.id));
const frameId = ea.generateElementId();
ea.style.fillStyle = "solid";
ea.style.roughness = 0;
ea.style.strokeColor = "transparent";
ea.style.strokeWidth = 0.1;
ea.style.opacity = 50;
let blackEl = ea.getViewElements().find(el=>el.id === "allblack");
let whiteEl = ea.getViewElements().find(el=>el.id === "whiteovr");
if(blackEl && whiteEl) {
ea.copyViewElementsToEAforEditing([blackEl, whiteEl]);
} else
if (blackEl && !whiteEl) {
ea.copyViewElementsToEAforEditing([blackEl]);
ea.style.backgroundColor = "white";
ea.addRect(frame.x,frame.y,frame.width,frame.height, "whiteovr");
} else
if (!blackEl && whiteEl) {
ea.style.backgroundColor = "black";
ea.addRect(frame.x-2,frame.y-2,frame.width+4,frame.height+4, "allblack");
ea.copyViewElementsToEAforEditing([whiteEl]);
} else {
ea.style.backgroundColor = "black";
ea.addRect(frame.x-2,frame.y-2,frame.width+4,frame.height+4, "allblack");
ea.style.backgroundColor = "white";
ea.addRect(frame.x,frame.y,frame.width,frame.height, "whiteovr");
}
blackEl = ea.getElement("allblack");
whiteEl = ea.getElement("whiteovr");
//this "magic" is required to ensure the frame element is above in sequence of the new rectangle elements
ea.getElements().forEach(el=>{el.frameId = frameId});
ea.copyViewElementsToEAforEditing(ea.getViewElements().filter(el=>el.id === frame.id));
const newFrame = ea.getElement(frame.id);
newFrame.id = frameId;
ea.elementsDict[frameId] = newFrame;
ea.copyViewElementsToEAforEditing(ea.getViewElements().filter(el=>el.id === frame.id));
ea.getElement(frame.id).isDeleted = true;
let curve = await utils.inputPrompt(
"Set roundess",
"Positive whole number",
`${whiteEl.roundness?.value ?? "500"}`
);
if(!curve) return;
curve = parseInt(curve);
if(isNaN(curve) || curve < 0) {
new Notice ("Roudness is not a valid positive whole number");
return;
}
whiteEl.roundness = {type: 3, value: curve};
ea.addElementsToView(false,false,true);

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-squircle"><path fill="none" d="M12 3c7.2 0 9 1.8 9 9s-1.8 9-9 9-9-1.8-9-9 1.8-9 9-9"/></svg>

After

Width:  |  Height:  |  Size: 294 B

15
ea-scripts/Custom Zoom.md Normal file
View File

@@ -0,0 +1,15 @@
/*
You can set a custom zoom level with this script. This allows you to set a zoom level below 10% or set the zoom level to a specific value. Note however, that Excalidraw has a bug under 10% zoom, and a phantom copy of your image may appear on screen. If this happens, increase the zoom and the phantom should disappear, if it doesn't then close and open the drawing.
```js*/
const api = ea.getExcalidrawAPI();
const appState = api.getAppState();
const zoomStr = await utils.inputPrompt("Zoom [%]",null,`${appState.zoom.value*100}%`);
if(!zoomStr) return;
const zoomNum = parseFloat(zoomStr.match(/^\d*/)[0]);
if(isNaN(zoomNum)) {
new Notice("You must provide a number");
return;
}
ea.getExcalidrawAPI().updateScene({appState:{zoom:{value: zoomNum/100 }}});

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-scan-search"><g stroke-width="2"><path d="M3 7V5a2 2 0 0 1 2-2h2"/><path d="M17 3h2a2 2 0 0 1 2 2v2"/><path d="M21 17v2a2 2 0 0 1-2 2h-2"/><path d="M7 21H5a2 2 0 0 1-2-2v-2"/><circle cx="12" cy="12" r="3"/><path d="m16 16-1.9-1.9"/></g></svg>

After

Width:  |  Height:  |  Size: 444 B

View File

@@ -3,13 +3,70 @@
Select some elements in the scene. The script will take these elements and move them into a new Excalidraw file, and open that file. The selected elements will also be replaced in your original drawing with the embedded Excalidraw file (the one that was just created). You will be prompted for the file name of the new deconstructed image. The script is useful if you want to break a larger drawing into smaller reusable parts that you want to reference in multiple drawings.
<iframe width="560" height="315" src="https://www.youtube.com/embed/HRtaaD34Zzg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<iframe width="560" height="315" src="https://www.youtube.com/embed/mvMQcz401yo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.7.29")) {
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.0.25")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
// -------------------------------
// Utility variables and functions
// -------------------------------
const excalidrawTemplates = ea.getListOfTemplateFiles();
if(typeof window.ExcalidrawDeconstructElements === "undefined") {
window.ExcalidrawDeconstructElements = {
openDeconstructedImage: true,
templatePath: excalidrawTemplates?.[0].path??""
};
}
const splitFolderAndFilename = (filepath) => {
const lastIndex = filepath.lastIndexOf("/");
return {
foldername: ea.obsidian.normalizePath(filepath.substring(0, lastIndex)),
filename: (lastIndex == -1 ? filepath : filepath.substring(lastIndex + 1)) + ".md"
};
}
let settings = ea.getScriptSettings();
//set default values on first run
if(!settings["Templates"]) {
settings = {
"Templates" : {
value: "",
description: "Comma-separated list of template filepaths"
}
};
await ea.setScriptSettings(settings);
}
if(!settings["Default file name"]) {
settings["Default file name"] = {
value: "deconstructed",
description: "The default filename to use when deconstructing elements."
};
await ea.setScriptSettings(settings);
}
const DEFAULT_FILENAME = settings["Default file name"].value;
const templates = settings["Templates"]
.value
.split(",")
.map(p=>app.metadataCache.getFirstLinkpathDest(p.trim(),""))
.concat(excalidrawTemplates)
.filter(f=>Boolean(f))
.sort((a,b) => a.basename.localeCompare(b.basename));
// ------------------------------------
// Prepare elements to be deconstructed
// ------------------------------------
const els = ea.getViewSelectedElements();
if (els.length === 0) {
new Notice("You must select elements first")
@@ -20,53 +77,126 @@ const bb = ea.getBoundingBox(els);
ea.copyViewElementsToEAforEditing(els);
ea.getElements().filter(el=>el.type==="image").forEach(el=>{
const img = ea.targetView.excalidrawData.getFile(el.fileId);
const path = (img?.linkParts?.original)??(img?.file?.path);
if(img && path) {
ea.imagesDict[el.fileId] = {
mimeType: img.mimeType,
id: el.fileId,
dataURL: img.img,
created: img.mtime,
file: path,
hasSVGwithBitmap: img.isSVGwithBitmap,
latex: null,
};
return;
}
const equation = ea.targetView.excalidrawData.getEquation(el.fileId);
eqImg = ea.targetView.getScene()?.files[el.fileId]
if(equation && eqImg) {
ea.imagesDict[el.fileId] = {
mimeType: eqImg.mimeType,
id: el.fileId,
dataURL: eqImg.dataURL,
created: eqImg.created,
file: null,
hasSVGwithBitmap: null,
latex: equation.latex,
};
return;
}
const img = ea.targetView.excalidrawData.getFile(el.fileId);
const path = (img?.linkParts?.original)??(img?.file?.path);
if(img && path) {
ea.imagesDict[el.fileId] = {
mimeType: img.mimeType,
id: el.fileId,
dataURL: img.img,
created: img.mtime,
file: path,
hasSVGwithBitmap: img.isSVGwithBitmap,
latex: null,
};
return;
}
const equation = ea.targetView.excalidrawData.getEquation(el.fileId);
eqImg = ea.targetView.getScene()?.files[el.fileId]
if(equation && eqImg) {
ea.imagesDict[el.fileId] = {
mimeType: eqImg.mimeType,
id: el.fileId,
dataURL: eqImg.dataURL,
created: eqImg.created,
file: null,
hasSVGwithBitmap: null,
latex: equation.latex,
};
return;
}
});
let folder = ea.targetView.file.path;
folder = folder.lastIndexOf("/")===-1?"":folder.substring(0,folder.lastIndexOf("/"))+"/";
const fname = await utils.inputPrompt("Filename for new file","Filename","");
const template = app.metadataCache.getFirstLinkpathDest(ea.plugin.settings.templateFilePath,"");
// ------------
// Input prompt
// ------------
let shouldAnchor = false;
const actionButtons = [
{
caption: "Insert @100%",
tooltip: "Anchor to 100% size",
action: () => {
shouldAnchor = true;
}
},
{
caption: "Insert",
tooltip: "Insert without anchoring",
action: () => {
shouldAnchor = false;
}
}];
const customControls = (container) => {
new ea.obsidian.Setting(container)
.setName(`Select template`)
.addDropdown(dropdown => {
templates.forEach(file => dropdown.addOption(file.path, file.basename));
if(templates.length === 0) dropdown.addOption(null, "none");
dropdown
.setValue(window.ExcalidrawDeconstructElements.templatePath)
.onChange(value => {
window.ExcalidrawDeconstructElements.templatePath = value;
})
})
new ea.obsidian.Setting(container)
.setName(`Open deconstructed image`)
.addToggle((toggle) => toggle
.setValue(window.ExcalidrawDeconstructElements.openDeconstructedImage)
.onChange(value => {
window.ExcalidrawDeconstructElements.openDeconstructedImage = value;
})
)
}
const path = await utils.inputPrompt(
"Filename for new file",
"Filename",
await ea.getAttachmentFilepath(DEFAULT_FILENAME),
actionButtons,
2,
false,
customControls
);
if(!path) return;
// ----------------------
// Execute deconstruction
// ----------------------
const {foldername, filename} = splitFolderAndFilename(path);
const newPath = await ea.create ({
filename: fname + ".md",
foldername: folder,
templatePath: template?.path,
onNewPane: true
filename,
foldername,
templatePath: window.ExcalidrawDeconstructElements.templatePath,
onNewPane: true,
silent: !window.ExcalidrawDeconstructElements.openDeconstructedImage
});
setTimeout(async ()=>{
const file = app.metadataCache.getFirstLinkpathDest(newPath,"")
ea.deleteViewElements(els);
ea.clear();
await ea.addImage(bb.topX,bb.topY,file,false);
await ea.addElementsToView(false, true, true);
ea.getExcalidrawAPI().history.clear(); //to avoid undo/redo messing up the decomposition
},1000);
let f = app.vault.getAbstractFileByPath(newPath);
let counter = 0;
while((!f || !ea.isExcalidrawFile(f)) && counter++<100) {
await sleep(50);
f = app.vault.getAbstractFileByPath(newPath);
}
if(!f || !ea.isExcalidrawFile(f)) {
new Notice("Something went wrong");
return;
}
let padding = parseFloat(app.metadataCache.getCache(f.path)?.frontmatter["excalidraw-export-padding"]);
if(isNaN(padding)) {
padding = ea.plugin.settings.exportPaddingSVG;
}
ea.getElements().forEach(el=>el.isDeleted = true);
await ea.addImage(bb.topX-padding,bb.topY-padding,f,false, shouldAnchor);
await ea.addElementsToView(false, true, true);
ea.getExcalidrawAPI().history.clear();
if(!window.ExcalidrawDeconstructElements.openDeconstructedImage) {
new Notice("Deconstruction ready");
}

View File

@@ -0,0 +1,61 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-ellipse-elements.png)
This script will add an encapsulating ellipse around the currently selected elements in Excalidraw.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.21")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
settings = ea.getScriptSettings();
//set default values on first run
if(!settings["Default padding"]) {
settings = {
"Prompt for padding?": true,
"Default padding" : {
value: 10,
description: "Padding between the bounding box of the selected elements, and the ellipse the script creates"
}
};
ea.setScriptSettings(settings);
}
let padding = settings["Default padding"].value;
if(settings["Prompt for padding?"]) {
padding = parseInt (await utils.inputPrompt("padding?","number",padding.toString()));
}
if(isNaN(padding)) {
new Notice("The padding value provided is not a number");
return;
}
elements = ea.getViewSelectedElements();
const box = ea.getBoundingBox(elements);
color = ea
.getExcalidrawAPI()
.getAppState()
.currentItemStrokeColor;
//uncomment for random color:
//color = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,"0");
ea.style.strokeColor = color;
const ellipseWidth = box.width/Math.sqrt(2);
const ellipseHeight = box.height/Math.sqrt(2);
const topX = box.topX - (ellipseWidth - box.width/2);
const topY = box.topY - (ellipseHeight - box.height/2);
id = ea.addEllipse(
topX - padding,
topY - padding,
2*ellipseWidth + 2*padding,
2*ellipseHeight + 2*padding
);
ea.copyViewElementsToEAforEditing(elements);
ea.addToGroup([id].concat(elements.map((el)=>el.id)));
ea.addElementsToView(false,false);

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

689
ea-scripts/ExcaliAI.md Normal file
View File

@@ -0,0 +1,689 @@
/*
<iframe width="560" height="315" src="https://www.youtube.com/embed/A1vrSGBbWgo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-draw-a-ui.jpg)
```js*/
let dirty=false;
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.0.12")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
const outputTypes = {
"html": {
instruction: "Turn this into a single html file using tailwind. Return a single message containing only the html file in a codeblock.",
blocktype: "html"
},
"mermaid": {
instruction: "Return a single message containing only the mermaid diagram in a codeblock.",
blocktype: "mermaid"
},
"svg": {
instruction: "Return a single message containing only the SVG code in an html codeblock.",
blocktype: "svg"
},
"image-gen": {
instruction: "Return a single message with the generated image prompt in a codeblock",
blocktype: "image"
},
"image-gen-silent": {
instruction: "Return a single message with the generated image prompt in a codeblock",
blocktype: "image-silent"
},
"image-edit": {
instruction: "",
blocktype: "image"
}
}
const systemPrompts = {
"Challenge my thinking": {
prompt: `Your task is to interpret a screenshot of a whiteboard, translating its ideas into a Mermaid graph. The whiteboard will encompass thoughts on a subject. Within the mind map, distinguish ideas that challenge, dispute, or contradict the whiteboard content. Additionally, include concepts that expand, complement, or advance the user's thinking. Utilize the Mermaid graph diagram type and present the resulting Mermaid diagram within a code block. Ensure the Mermaid script excludes the use of parentheses ().`,
type: "mermaid",
help: "Translate your image and optional text prompt into a Mermaid mindmap. If there are conversion errors, edit the Mermaid script under 'More Tools'."
},
"Convert sketch to shapes": {
prompt: `Given an image featuring various geometric shapes drawn by the user, your objective is to analyze the input and generate SVG code that accurately represents these shapes. Your output will be the SVG code enclosed in an HTML code block.`,
type: "svg",
help: "Convert selected scribbles into shapes; works better with fewer shapes. Experimental and may not produce good drawings."
},
"Create a simple Excalidraw icon": {
prompt: `Given a description of an SVG image from the user, your objective is to generate the corresponding SVG code. Avoid incorporating textual elements within the generated SVG. Your output should be the resulting SVG code enclosed in an HTML code block.`,
type: "svg",
help: "Convert text prompts into simple icons inserted as Excalidraw elements. Expect only a text prompt. Experimental and may not produce good drawings."
},
"Create a stick figure": {
prompt: "You will receive a prompt from the user. Your task involves drawing a simple stick figure or a scene involving a few stick figures based on the user's prompt. Create the stickfigure based on the following style description. DO NOT add any detail, just use it AS-IS: Create a simple stick figure character with a large round head and a face in the style of sketchy caricatures. The stick figure should have a rudimentary body composed of straight lines representing the arms and legs. Hands and toes should be should be represented with round shapes, do not add details such as fingers or toes. Use fine lines, smooth curves, rounded shapes. The stick figure should retain a playful and childlike simplicity, reminiscent of a doodle someone might draw on the corner of a notebook page. Create a black and white drawing, a hand-drawn figure on white background.",
type: "image-gen",
help: "Send only the text prompt to OpenAI. Provide a detailed description; OpenAI will enrich your prompt automatically. To avoid it, start your prompt like this 'DO NOT add any detail, just use it AS-IS:'"
},
"Edit an image": {
prompt: null,
type: "image-edit",
help: "Image elements will be used as the Image. Shapes on top of the image will be the Mask. Use the prompt to instruct Dall-e about the changes. Dall-e-2 model will be used."
},
"Generate an image from image and prompt": {
prompt: "Your task involves receiving an image and a textual prompt from the user. Your goal is to craft a detailed, accurate, and descriptive narrative of the image, tailored for effective image generation. Utilize the user-provided text prompt to inform and guide your depiction of the image. Ensure the resulting image remains text-free.",
type: "image-gen",
help: "Generate an image based on the drawing and prompt using ChatGPT-Vision and Dall-e. Provide a contextual text-prompt for accurate interpretation."
},
"Generate an image from prompt": {
prompt: null,
type: "image-gen",
help: "Send only the text prompt to OpenAI. Provide a detailed description; OpenAI will enrich your prompt automatically. To avoid it, start your prompt like this 'DO NOT add any detail, just use it AS-IS:'"
},
"Generate an image to illustrate a quote": {
prompt: "Your task involves transforming a user-provided quote into a detailed and imaginative illustration. Craft a visual representation that captures the essence of the quote and resonates well with a broad audience. If the Author's name is provided, aim to establish a connection between the illustration and the Author. This can be achieved by referencing a well-known story from the Author, situating the image in the Author's era or setting, or employing other creative methods of association. Additionally, provide preferences for styling, such as the chosen medium and artistic direction, to guide the image creation process. Ensure the resulting image remains text-free. Your task output should comprise a descriptive and detailed narrative aimed at facilitating the creation of a captivating illustration from the quote.",
type: "image-gen",
help: "ExcaliAI will create an image prompt to illustrate your text input - a quote - with GPT, then generate an image using Dall-e. In case you include the Author's name, GPT will try to generate an image that in some way references the Author."
},
"Generate 4 icon-variants based on input image": {
prompt: "Given a simple sketch and an optional text prompt from the user, your task is to generate a descriptive narrative tailored for effective image generation, capturing the style of the sketch. Utilize the text prompt to guide the description. Your objective is to instruct DALL-E to create a collage of four minimalist black and white hand-drawn pencil sketches in a 2x2 matrix format. Each sketch should convert the user's sketch into simple artistic SVG icons with transparent backgrounds. Ensure the resulting images remain text-free, maintaining a minimalist, easy-to-understand style, and omit framing borders. Only include a pencil in the drawing if it is explicitely metioned in the user prompt or included in the sketch.",
type: "image-gen-silent",
help: "Generate a collage of 4 icons based on the drawing using ChatGPT-Vision and Dall-e. You may provide a contextual text-prompt to improve accuracy of interpretation."
},
"Visual brainstorm": {
prompt: "Your objective is to interpret a screenshot of a whiteboard, creating an image aimed at sparking further thoughts on the subject. The whiteboard will present diverse ideas about a specific topic. Your generated image should achieve one of two purposes: highlighting concepts that challenge, dispute, or contradict the whiteboard content, or introducing ideas that expand, complement, or enrich the user's thinking. You have the option to include multiple tiles in the resulting image, resembling a sequence akin to a comic strip. Ensure that the image remains devoid of text.",
type: "image-gen",
help: "Use ChatGPT Visions and Dall-e to create an image based on your text prompt and image to spark new ideas."
},
"Wireframe to code": {
prompt: `You are an expert tailwind developer. A user will provide you with a low-fidelity wireframe of an application and you will return a single html file that uses tailwind to create the website. Use creative license to make the application more fleshed out. Write the necessary javascript code. If you need to insert an image, use placehold.co to create a placeholder image.`,
type: "html",
help: "Use GPT Visions to interpret the wireframe and generate a web application. YOu may copy the resulting code from the active embeddable's top left menu."
},
}
const IMAGE_WARNING = "The generated image is linked through a temporary OpenAI URL and will be removed in approximately 30 minutes. To save it permanently, choose 'Save image from URL to local file' from the Obsidian Command Palette."
// --------------------------------------
// Initialize values and settings
// --------------------------------------
let settings = ea.getScriptSettings();
if(!settings["Agent's Task"]) {
settings = {
"Agent's Task": "Wireframe to code",
"User Prompt": "",
};
await ea.setScriptSettings(settings);
}
const OPENAI_API_KEY = ea.plugin.settings.openAIAPIToken;
if(!OPENAI_API_KEY || OPENAI_API_KEY === "") {
new Notice("You must first configure your API key in Excalidraw Plugin Settings");
return;
}
let userPrompt = settings["User Prompt"] ?? "";
let agentTask = settings["Agent's Task"];
let imageSize = settings["Image Size"]??"1024x1024";
if(!systemPrompts.hasOwnProperty(agentTask)) {
agentTask = Object.keys(systemPrompts)[0];
}
let imageModel, valideSizes;
const setImageModelAndSizes = () => {
imageModel = systemPrompts[agentTask].type === "image-edit"
? "dall-e-2"
: ea.plugin.settings.openAIDefaultImageGenerationModel;
validSizes = imageModel === "dall-e-2"
? [`256x256`, `512x512`, `1024x1024`]
: (imageModel === "dall-e-3"
? [`1024x1024`, `1792x1024`, `1024x1792`]
: [`1024x1024`])
if(!validSizes.includes(imageSize)) {
imageSize = "1024x1024";
dirty = true;
}
}
setImageModelAndSizes();
// --------------------------------------
// Generate Image Blob From Selected Excalidraw Elements
// --------------------------------------
const calculateImageScale = (elements) => {
const bb = ea.getBoundingBox(elements);
const size = (bb.width*bb.height);
const minRatio = Math.sqrt(360000/size);
const maxRatio = Math.sqrt(size/16000000);
return minRatio > 1
? minRatio
: (
maxRatio > 1
? 1/maxRatio
: 1
);
}
const createMask = async (dataURL) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
// If opaque (alpha > 0), make it transparent
if (data[i + 3] > 0) {
data[i + 3] = 0; // Set alpha to 0 (transparent)
} else if (data[i + 3] === 0) {
// If fully transparent, make it red
data[i] = 255; // Red
data[i + 1] = 0; // Green
data[i + 2] = 0; // Blue
data[i + 3] = 255; // make it opaque
}
}
ctx.putImageData(imageData, 0, 0);
const maskDataURL = canvas.toDataURL();
resolve(maskDataURL);
};
img.onerror = error => {
reject(error);
};
img.src = dataURL;
});
}
//https://platform.openai.com/docs/api-reference/images/createEdit
//dall-e-2 image edit only works on square images
//if targetDalleImageEdit === true then the image and the mask will be returned in two separate dataURLs
let squareBB;
const generateCanvasDataURL = async (view, targetDalleImageEdit=false) => {
let PADDING = 5;
await view.forceSave(true); //to ensure recently embedded PNG and other images are saved to file
const viewElements = ea.getViewSelectedElements();
if(viewElements.length === 0) {
return {imageDataURL: null, maskDataURL: null} ;
}
ea.copyViewElementsToEAforEditing(viewElements, true); //copying the images objects over to EA for PNG generation
let maskDataURL;
const loader = ea.getEmbeddedFilesLoader(false);
let scale = calculateImageScale(ea.getElements());
const bb = ea.getBoundingBox(viewElements);
if(ea.getElements()
.filter(el=>el.type==="image")
.some(el=>Math.round(el.width) === Math.round(bb.width) && Math.round(el.height) === Math.round(bb.height))
) { PADDING = 0; }
let exportSettings = {withBackground: true, withTheme: true};
if(targetDalleImageEdit) {
PADDING = 0;
const strokeColor = ea.style.strokeColor;
const backgroundColor = ea.style.backgroundColor;
ea.style.backgroundColor = "transparent";
ea.style.strokeColor = "transparent";
let rectID;
if(bb.height > bb.width) {
rectID = ea.addRect(bb.topX-(bb.height-bb.width)/2, bb.topY,bb.height, bb.height);
}
if(bb.width > bb.height) {
rectID = ea.addRect(bb.topX, bb.topY-(bb.width-bb.height)/2,bb.width, bb.width);
}
if(bb.height === bb.width) {
rectID = ea.addRect(bb.topX, bb.topY, bb.width, bb.height);
}
const rect = ea.getElement(rectID);
squareBB = {topX: rect.x-PADDING, topY: rect.y-PADDING, width: rect.width + 2*PADDING, height: rect.height + 2*PADDING};
ea.style.strokeColor = strokeColor;
ea.style.backgroundColor = backgroundColor;
ea.getElements().filter(el=>el.type === "image").forEach(el=>{el.isDeleted = true});
dalleWidth = parseInt(imageSize.split("x")[0]);
scale = dalleWidth/squareBB.width;
exportSettings = {withBackground: false, withTheme: true};
maskDataURL= await ea.createPNGBase64(
null, scale, exportSettings, loader, "light", PADDING
);
maskDataURL = await createMask(maskDataURL)
ea.getElements().filter(el=>el.type === "image").forEach(el=>{el.isDeleted = false});
ea.getElements().filter(el=>el.type !== "image" && el.id !== rectID).forEach(el=>{el.isDeleted = true});
}
const imageDataURL = await ea.createPNGBase64(
null, scale, exportSettings, loader, "light", PADDING
);
ea.clear();
return {imageDataURL, maskDataURL};
}
let {imageDataURL, maskDataURL} = await generateCanvasDataURL(ea.targetView, systemPrompts[agentTask].type === "image-edit");
// --------------------------------------
// Support functions - embeddable spinner and error
// --------------------------------------
const spinner = await ea.convertStringToDataURL(`
<html><head><style>
html, body {width: 100%; height: 100%; color: ${ea.getExcalidrawAPI().getAppState().theme === "dark" ? "white" : "black"};}
body {display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 1rem; overflow: hidden;}
.Spinner {display: flex; align-items: center; justify-content: center; margin-left: auto; margin-right: auto;}
.Spinner svg {animation: rotate 1.6s linear infinite; transform-origin: center center; width: 40px; height: 40px;}
.Spinner circle {stroke: currentColor; animation: dash 1.6s linear 0s infinite; stroke-linecap: round;}
@keyframes rotate {100% {transform: rotate(360deg);}}
@keyframes dash {
0% {stroke-dasharray: 1, 300; stroke-dashoffset: 0;}
50% {stroke-dasharray: 150, 300; stroke-dashoffset: -200;}
100% {stroke-dasharray: 1, 300; stroke-dashoffset: -280;}
}
</style></head><body>
<div class="Spinner">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="46" stroke-width="8" fill="none" stroke-miter-limit="10"/>
</svg>
</div>
<div>Generating...</div>
</body></html>`);
const errorMessage = async (spinnerID, message) => {
const error = "Something went wrong! Check developer console for more.";
const details = message ? `<p>${message}</p>` : "";
const errorDataURL = await ea.convertStringToDataURL(`
<html><head><style>
html, body {height: 100%;}
body {display: flex; flex-direction: column; align-items: center; justify-content: center; color: red;}
h1, h3 {margin-top: 0;margin-bottom: 0.5rem;}
</style></head><body>
<h1>Error!</h1>
<h3>${error}</h3>${details}
</body></html>`);
new Notice (error);
ea.getElement(spinnerID).link = errorDataURL;
ea.addElementsToView(false,true);
}
// --------------------------------------
// Utility to write Mermaid to dialog
// --------------------------------------
const EDITOR_LS_KEYS = {
OAI_API_KEY: "excalidraw-oai-api-key",
MERMAID_TO_EXCALIDRAW: "mermaid-to-excalidraw",
PUBLISH_LIBRARY: "publish-library-data",
};
const setMermaidDataToStorage = (mermaidDefinition) => {
try {
window.localStorage.setItem(
EDITOR_LS_KEYS.MERMAID_TO_EXCALIDRAW,
JSON.stringify(mermaidDefinition)
);
return true;
} catch (error) {
console.warn(`localStorage.setItem error: ${error.message}`);
return false;
}
};
// --------------------------------------
// Submit Prompt
// --------------------------------------
const generateImage = async(text, spinnerID, bb, silent=false) => {
const requestObject = {
text,
imageGenerationProperties: {
size: imageSize,
//quality: "standard", //not supported by dall-e-2
n:1,
},
};
const result = await ea.postOpenAI(requestObject);
console.log({result, json:result?.json});
if(!result?.json?.data?.[0]?.url) {
await errorMessage(spinnerID, result?.json?.error?.message);
return;
}
const spinner = ea.getElement(spinnerID)
spinner.isDeleted = true;
const imageID = await ea.addImage(spinner.x, spinner.y, result.json.data[0].url);
const imageEl = ea.getElement(imageID);
const revisedPrompt = result.json.data[0].revised_prompt;
if(revisedPrompt && !silent) {
ea.style.fontSize = 16;
const rectID = ea.addText(imageEl.x+15, imageEl.y + imageEl.height + 50, revisedPrompt, {
width: imageEl.width-30,
textAlign: "center",
textVerticalAlign: "top",
box: true,
})
ea.getElement(rectID).strokeColor = "transparent";
ea.getElement(rectID).backgroundColor = "transparent";
ea.addToGroup(ea.getElements().filter(el=>el.id !== spinnerID).map(el=>el.id));
}
await ea.addElementsToView(false, true, true);
if(silent) return;
ea.getExcalidrawAPI().setToast({
message: IMAGE_WARNING,
duration: 15000,
closable: true
});
}
const run = async (text) => {
if(!text && !imageDataURL) {
new Notice("No prompt, aborting");
return;
}
const systemPrompt = systemPrompts[agentTask];
const outputType = outputTypes[systemPrompt.type];
const isImageGenRequest = outputType.blocktype === "image" || outputType.blocktype === "image-silent";
const isImageEditRequest = systemPrompt.type === "image-edit";
if(isImageEditRequest) {
if(!text) {
new Notice("You must provide a text prompt with instructions for how the image should be modified");
return;
}
if(!imageDataURL || !maskDataURL) {
new Notice("You must provide an image and a mask");
return;
}
}
//place spinner next to selected elements
const bb = ea.getBoundingBox(ea.getViewSelectedElements());
const spinnerID = ea.addEmbeddable(bb.topX+bb.width+100,bb.topY-(720-bb.height)/2,550,720,spinner);
//this block is in an async call using the isEACompleted flag because otherwise during debug Obsidian
//goes black (not freezes, but does not get a new frame for some reason)
//palcing this in an async call solves this issue
//If you know why this is happening and can offer a better solution, please reach out to @zsviczian
let isEACompleted = false;
setTimeout(async()=>{
await ea.addElementsToView(false,true);
ea.clear();
const embeddable = ea.getViewElements().filter(el=>el.id===spinnerID);
ea.copyViewElementsToEAforEditing(embeddable);
const els = ea.getViewSelectedElements();
ea.viewZoomToElements(false, els.concat(embeddable));
isEACompleted = true;
});
if(isImageGenRequest && !systemPrompt.prompt && !isImageEditRequest) {
generateImage(text,spinnerID,bb);
return;
}
const requestObject = isImageEditRequest
? {
...imageDataURL ? {image: {url: imageDataURL}} : {},
...(text && text.trim() !== "") ? {text} : {},
imageGenerationProperties: {
size: imageSize,
//quality: "standard", //not supported by dall-e-2
n:1,
mask: maskDataURL,
},
}
: {
...imageDataURL ? {image: {url: imageDataURL}} : {},
...(text && text.trim() !== "") ? {text} : {},
systemPrompt: systemPrompt.prompt,
instruction: outputType.instruction,
}
//Get result from GPT
const result = await ea.postOpenAI(requestObject);
console.log({result, json:result?.json});
//checking that EA has completed. Because the postOpenAI call is an async await
//I don't expect EA not to be completed by now. However the devil never sleeps.
//This (the insomnia of the Devil) is why I have a watchdog here as well
let counter = 0
while(!isEACompleted && counter++<10) sleep(50);
if(!isEACompleted) {
await errorMessage(spinnerID, "Unexpected issue with ExcalidrawAutomate");
return;
}
if(isImageEditRequest) {
if(!result?.json?.data?.[0]?.url) {
await errorMessage(spinnerID, result?.json?.error?.message);
return;
}
const spinner = ea.getElement(spinnerID)
spinner.isDeleted = true;
const imageID = await ea.addImage(spinner.x, spinner.y, result.json.data[0].url);
await ea.addElementsToView(false, true, true);
ea.getExcalidrawAPI().setToast({
message: IMAGE_WARNING,
duration: 15000,
closable: true
});
return;
}
if(!result?.json?.hasOwnProperty("choices")) {
await errorMessage(spinnerID, result?.json?.error?.message);
return;
}
//exctract codeblock and display result
let content = ea.extractCodeBlocks(result.json.choices[0]?.message?.content)[0]?.data;
if(!content) {
await errorMessage(spinnerID);
return;
}
if(isImageGenRequest) {
generateImage(content,spinnerID,bb,outputType.blocktype === "image-silent");
return;
}
switch(outputType.blocktype) {
case "html":
ea.getElement(spinnerID).link = await ea.convertStringToDataURL(content);
ea.addElementsToView(false,true);
break;
case "svg":
ea.getElement(spinnerID).isDeleted = true;
ea.importSVG(content);
ea.addToGroup(ea.getElements().map(el=>el.id));
if(ea.getViewSelectedElements().length>0) {
ea.targetView.currentPosition = {x: bb.topX+bb.width+100, y: bb.topY};
}
ea.addElementsToView(true, false);
break;
case "mermaid":
if(content.startsWith("mermaid")) {
content = content.replace(/^mermaid/,"").trim();
}
try {
result = await ea.addMermaid(content);
if(typeof result === "string") {
await errorMessage(spinnerID, "Open [More Tools / Mermaid to Excalidraw] to manually fix the received mermaid script<br><br>" + result);
return;
}
} catch (e) {
ea.addText(0,0,content);
}
ea.getElement(spinnerID).isDeleted = true;
ea.targetView.currentPosition = {x: bb.topX+bb.width+100, y: bb.topY-bb.height};
await ea.addElementsToView(true, false);
setMermaidDataToStorage(content);
new Notice("Open More Tools/Mermaid to Excalidraw in the top tools menu to edit the generated diagram",8000);
break;
}
}
// --------------------------------------
// User Interface
// --------------------------------------
let previewDiv;
const fragWithHTML = (html) => createFragment((frag) => (frag.createDiv().innerHTML = html));
const isImageGenerationTask = () => systemPrompts[agentTask].type === "image-gen" || systemPrompts[agentTask].type === "image-gen-silent" || systemPrompts[agentTask].type === "image-edit";
const addPreviewImage = () => {
if(!previewDiv) return;
previewDiv.empty();
previewDiv.createEl("img",{
attr: {
style: `max-width: 100%;max-height: 30vh;`,
src: imageDataURL,
}
});
if(maskDataURL) {
previewDiv.createEl("img",{
attr: {
style: `max-width: 100%;max-height: 30vh;`,
src: maskDataURL,
}
});
}
}
const configModal = new ea.obsidian.Modal(app);
configModal.modalEl.style.width="100%";
configModal.modalEl.style.maxWidth="1000px";
configModal.onOpen = async () => {
const contentEl = configModal.contentEl;
contentEl.createEl("h1", {text: "ExcaliAI"});
let systemPromptTextArea, systemPromptDiv, imageSizeSetting, imageSizeSettingDropdown, helpEl;
new ea.obsidian.Setting(contentEl)
.setName("What would you like to do?")
.addDropdown(dropdown=>{
Object.keys(systemPrompts).forEach(key=>dropdown.addOption(key,key));
dropdown
.setValue(agentTask)
.onChange(async (value) => {
dirty = true;
const prevTask = agentTask;
agentTask = value;
if(
(systemPrompts[prevTask].type === "image-edit" && systemPrompts[value].type !== "image-edit") ||
(systemPrompts[prevTask].type !== "image-edit" && systemPrompts[value].type === "image-edit")
) {
({imageDataURL, maskDataURL} = await generateCanvasDataURL(ea.targetView, systemPrompts[value].type === "image-edit"));
addPreviewImage();
setImageModelAndSizes();
while (imageSizeSettingDropdown.selectEl.options.length > 0) { imageSizeSettingDropdown.selectEl.remove(0); }
validSizes.forEach(size=>imageSizeSettingDropdown.addOption(size,size));
imageSizeSettingDropdown.setValue(imageSize);
}
imageSizeSetting.settingEl.style.display = isImageGenerationTask() ? "" : "none";
const prompt = systemPrompts[value].prompt;
helpEl.innerHTML = `<b>Help: </b>` + systemPrompts[value].help;
if(prompt) {
systemPromptDiv.style.display = "";
systemPromptTextArea.setValue(systemPrompts[value].prompt);
} else {
systemPromptDiv.style.display = "none";
}
});
})
helpEl = contentEl.createEl("p");
helpEl.innerHTML = `<b>Help: </b>` + systemPrompts[agentTask].help;
systemPromptDiv = contentEl.createDiv();
systemPromptDiv.createEl("h4", {text: "Customize System Prompt"});
systemPromptDiv.createEl("span", {text: "Unless you know what you are doing I do not recommend changing the system prompt"})
const systemPromptSetting = new ea.obsidian.Setting(systemPromptDiv)
.addTextArea(text => {
systemPromptTextArea = text;
const prompt = systemPrompts[agentTask].prompt;
text.inputEl.style.minHeight = "10em";
text.inputEl.style.width = "100%";
text.setValue(prompt);
text.onChange(value => {
systemPrompts[value].prompt = value;
});
if(!prompt) systemPromptDiv.style.display = "none";
})
systemPromptSetting.nameEl.style.display = "none";
systemPromptSetting.descEl.style.display = "none";
systemPromptSetting.infoEl.style.display = "none";
contentEl.createEl("h4", {text: "User Prompt"});
const userPromptSetting = new ea.obsidian.Setting(contentEl)
.addTextArea(text => {
text.inputEl.style.minHeight = "10em";
text.inputEl.style.width = "100%";
text.setValue(userPrompt);
text.onChange(value => {
userPrompt = value;
dirty = true;
})
})
userPromptSetting.nameEl.style.display = "none";
userPromptSetting.descEl.style.display = "none";
userPromptSetting.infoEl.style.display = "none";
imageSizeSetting = new ea.obsidian.Setting(contentEl)
.setName("Select image size")
.setDesc(fragWithHTML("<mark>⚠️ Important ⚠️</mark>: " + IMAGE_WARNING))
.addDropdown(dropdown=>{
validSizes.forEach(size=>dropdown.addOption(size,size));
imageSizeSettingDropdown = dropdown;
dropdown
.setValue(imageSize)
.onChange(async (value) => {
dirty = true;
imageSize = value;
if(systemPrompts[agentTask].type === "image-edit") {
({imageDataURL, maskDataURL} = await generateCanvasDataURL(ea.targetView, true));
addPreviewImage();
}
});
})
imageSizeSetting.settingEl.style.display = isImageGenerationTask() ? "" : "none";
if(imageDataURL) {
previewDiv = contentEl.createDiv({
attr: {
style: "text-align: center;",
}
});
addPreviewImage();
} else {
contentEl.createEl("h4", {text: "No elements are selected from your canvas"});
contentEl.createEl("span", {text: "Because there are no Excalidraw elements selected on the canvas, only the text prompt will be sent to OpenAI."});
}
new ea.obsidian.Setting(contentEl)
.addButton(button =>
button
.setButtonText("Run")
.onClick((event)=>{
run(userPrompt); //Obsidian crashes otherwise, likely has to do with requesting an new frame for react
configModal.close();
})
);
}
configModal.onClose = () => {
if(dirty) {
settings["User Prompt"] = userPrompt;
settings["Agent's Task"] = agentTask;
settings["Image Size"] = imageSize;
ea.setScriptSettings(settings);
}
}
configModal.open();

1
ea-scripts/ExcaliAI.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M320 0c17.7 0 32 14.3 32 32V96H472c39.8 0 72 32.2 72 72V440c0 39.8-32.2 72-72 72H168c-39.8 0-72-32.2-72-72V168c0-39.8 32.2-72 72-72H288V32c0-17.7 14.3-32 32-32zM208 384c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H208zm96 0c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H304zm96 0c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H400zM264 256a40 40 0 1 0 -80 0 40 40 0 1 0 80 0zm152 40a40 40 0 1 0 0-80 40 40 0 1 0 0 80zM48 224H64V416H48c-26.5 0-48-21.5-48-48V272c0-26.5 21.5-48 48-48zm544 0c26.5 0 48 21.5 48 48v96c0 26.5-21.5 48-48 48H576V224h16z"/></svg>

After

Width:  |  Height:  |  Size: 694 B

View File

@@ -0,0 +1,259 @@
/*
Generates a hierarchical Markdown document out of a visual layout of an article.
Watch this video to understand how the script is intended to work:
![Excalidraw Writing Machine YouTube Video](https://youtu.be/zvRpCOZAUSs)
You can download the sample Obsidian Templater file from [here](https://gist.github.com/zsviczian/bf49d4b2d401f5749aaf8c2fa8a513d9)
You can download the demo PDF document showcased in the video from [here](https://zsviczian.github.io/DemoArticle-AtomicHabits.pdf)
```js*/
const selectedElements = ea.getViewSelectedElements();
if (selectedElements.length !== 1 || selectedElements[0].type === "arrow") {
new Notice("Select a single element that is not an arrow and not a frame");
return;
}
const visited = new Set(); // Avoiding recursive infinite loops
delete window.ewm;
await ea.targetView.save();
//------------------
// Load Settings
//------------------
let settings = ea.getScriptSettings();
//set default values on first run
let didSettingsChange = false;
if(!settings["Template path"]) {
settings = {
"Template path" : {
value: "",
description: "The template file path that will receive the concatenated text. If the file includes <<<REPLACE ME>>> then it will be replaced with the generated text, if <<<REPLACE ME>>> is not present in the file the hierarchical markdown generated from the diagram will be added to the end of the template."
},
"ZK '# Summary' section": {
value: "Summary",
description: "The section in your visual zettelkasten file that contains the short written summary of the idea. This is the text that will be included in the hierarchical markdown file if visual ZK cards are included in your flow"
},
"ZK '# Source' section": {
value: "Source",
description: "The section in your visual zettelkasten file that contains the reference to your source. If present in the file, this text will be included in the output file as a reference"
},
"Embed image links": {
value: true,
description: "Should the resulting markdown document include the ![[embedded images]]?"
}
};
didSettingsChange = true;
}
if(!settings["Generate ![markdown](links)"]) {
settings["Generate ![markdown](links)"] = {
value: true,
description: "If you turn this off the script will generate ![[wikilinks]] for images"
}
didSettingsChange = true;
}
if(didSettingsChange) {
await ea.setScriptSettings(settings);
}
const ZK_SOURCE = settings["ZK '# Source' section"].value;
const ZK_SECTION = settings["ZK '# Summary' section"].value;
const INCLUDE_IMG_LINK = settings["Embed image links"].value;
const MARKDOWN_LINKS = settings["Generate ![markdown](links)"].value;
let templatePath = settings["Template path"].value;
//------------------
// Select template file
//------------------
const MSG = "Select another file"
let selection = MSG;
if(templatePath && app.vault.getAbstractFileByPath(templatePath)) {
selection = await utils.suggester([templatePath, MSG],[templatePath, MSG], "Use previous template or select another?");
if(!selection) {
new Notice("process aborted");
return;
}
}
if(selection === MSG) {
const files = app.vault.getMarkdownFiles().map(f=>f.path);
selection = await utils.suggester(files,files,"Select the template to use. ESC to not use a tempalte");
}
if(selection && selection !== templatePath) {
settings["Template path"].value = selection;
await ea.setScriptSettings(settings);
}
templatePath = selection;
//------------------
// supporting functions
//------------------
function getNextElementFollowingArrow(el, arrow) {
if (arrow.startBinding?.elementId === el.id) {
return ea.getViewElements().find(x => x.id === arrow.endBinding?.elementId);
}
if (arrow.endBinding?.elementId === el.id) {
return ea.getViewElements().find(x => x.id === arrow.startBinding?.elementId);
}
return null;
}
function getImageLink(f) {
if(MARKDOWN_LINKS) {
return `![${f.basename}](${encodeURI(f.path)})`;
}
return `![[${f.path}|${f.basename}]]`;
}
function getBoundText(el) {
const textId = el.boundElements?.find(x => x.type === "text")?.id;
const text = ea.getViewElements().find(x => x.id === textId)?.originalText;
return text ? text + "\n" : "";
}
async function getSectionText(file, section) {
const content = await app.vault.cachedRead(file);
const metadata = app.metadataCache.getFileCache(file);
if (!metadata || !metadata.headings) {
return null;
}
const targetHeading = metadata.headings.find(h => h.heading === section);
if (!targetHeading) {
return null;
}
const startPos = targetHeading.position.start.offset;
let endPos = content.length;
const nextHeading = metadata.headings.find(h => h.position.start.offset > startPos);
if (nextHeading) {
endPos = nextHeading.position.start.offset;
}
let sectionContent = content.slice(startPos, endPos).trim();
sectionContent = sectionContent.substring(sectionContent.indexOf('\n') + 1).trim();
// Remove Markdown comments enclosed in %%
sectionContent = sectionContent.replace(/%%[\s\S]*?%%/g, '').trim();
return sectionContent;
}
async function getBlockText(file, blockref) {
const content = await app.vault.cachedRead(file);
const blockPattern = new RegExp(`\\^${blockref}\\b`, 'g');
let blockPosition = content.search(blockPattern);
if (blockPosition === -1) {
return "";
}
const startPos = content.lastIndexOf('\n', blockPosition) + 1;
let endPos = content.indexOf('\n', blockPosition);
if (endPos === -1) {
endPos = content.length;
} else {
const nextBlockOrHeading = content.slice(endPos).search(/(^# |^\^|\n)/gm);
if (nextBlockOrHeading !== -1) {
endPos += nextBlockOrHeading;
} else {
endPos = content.length;
}
}
let blockContent = content.slice(startPos, endPos).trim();
blockContent = blockContent.replace(blockPattern, '').trim();
blockContent = blockContent.replace(/%%[\s\S]*?%%/g, '').trim();
return blockContent;
}
async function getElementText(el) {
if (el.type === "text") {
return el.originalText;
}
if (el.type === "image") {
const f = ea.getViewFileForImageElement(el);
if(!ea.isExcalidrawFile(f)) return f.name + (INCLUDE_IMG_LINK ? `\n${getImageLink(f)}\n` : "");
let source = await getSectionText(f, ZK_SOURCE);
source = source ? ` (source:: ${source})` : "";
const summary = await getSectionText(f, ZK_SECTION) ;
if(summary) return (INCLUDE_IMG_LINK ? `${getImageLink(f)}\n${summary + source}` : summary + source) + "\n";
return f.name + (INCLUDE_IMG_LINK ? `\n${getImageLink(f)}\n` : "");
}
if (el.type === "embeddable") {
const linkWithRef = el.link.match(/\[\[([^\]]*)]]/)?.[1];
if(!linkWithRef) return "";
const path = linkWithRef.split("#")[0];
const f = app.metadataCache.getFirstLinkpathDest(path, ea.targetView.file.path);
if(!f) return "";
if(f.extension !== "md") return f.name;
const ref = linkWithRef.split("#")[1];
if(!ref) return await app.vault.read(f);
if(ref.startsWith("^")) {
return await getBlockText(f, ref.substring(1));
} else {
return await getSectionText(f, ref);
}
}
return getBoundText(el);
}
//------------------
// Navigating the hierarchy
//------------------
async function crawl(el, level, isFirst = false) {
visited.add(el.id);
let result = await getElementText(el) + "\n";
// Process all arrows connected to this element
const boundElementsData = el.boundElements.filter(x => x.type === "arrow");
const isFork = boundElementsData.length > (isFirst ? 1 : 2);
if(isFork) level++;
for(const bindingData of boundElementsData) {
const arrow = ea.getViewElements().find(x=> x.id === bindingData.id);
const nextEl = getNextElementFollowingArrow(el, arrow);
if (nextEl && !visited.has(nextEl.id)) {
if(isFork) result += `\n${"#".repeat(level)} `;
const arrowLabel = getBoundText(arrow);
if (arrowLabel) {
// If the arrow has a label, add it as an additional level
result += arrowLabel + "\n";
result += await crawl(nextEl, level);
} else {
// If no label, continue to the next element
result += await crawl(nextEl, level);
}
}
};
return result;
}
window.ewm = "## " + await crawl(selectedElements[0], 2, true);
const outputPath = await ea.getAttachmentFilepath(`EWM - ${ea.targetView.file.name}.md`);
let result = templatePath
? await app.vault.read(app.vault.getAbstractFileByPath(templatePath))
: "";
if(result.match("<<<REPLACE ME>>>")) {
result = result.replaceAll("<<<REPLACE ME>>>",window.ewm);
} else {
result += window.ewm;
}
const outfile = await app.vault.create(outputPath,result);
setTimeout(()=>{
ea.openFileInNewOrAdjacentLeaf(outfile);
}, 250);

View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="CurrentColor" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-keyboard">
<path stroke-width="2" d="M10 8h.01"/>
<path stroke-width="2" d="M12 12h.01"/>
<path stroke-width="2" d="M14 8h.01"/>
<path stroke-width="2" d="M16 12h.01"/>
<path stroke-width="2" d="M18 8h.01"/>
<path stroke-width="2" d="M6 8h.01"/>
<path stroke-width="2" d="M7 16h10"/>
<path stroke-width="2" d="M8 12h.01"/>
<path fill="none" stroke-width="2" d="M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z"/>
</svg>

After

Width:  |  Height:  |  Size: 611 B

673
ea-scripts/GPT-Draw-a-UI.md Normal file
View File

@@ -0,0 +1,673 @@
/*
<iframe width="560" height="315" src="https://www.youtube.com/embed/A1vrSGBbWgo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-draw-a-ui.jpg)
```js*/
let dirty=false;
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.0.12")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
const outputTypes = {
"html": {
instruction: "Turn this into a single html file using tailwind. Return a single message containing only the html file in a codeblock.",
blocktype: "html"
},
"mermaid": {
instruction: "Return a single message containing only the mermaid diagram in a codeblock.",
blocktype: "mermaid"
},
"svg": {
instruction: "Return a single message containing only the SVG code in an html codeblock.",
blocktype: "svg"
},
"image-gen": {
instruction: "Return a single message with the generated image prompt in a codeblock",
blocktype: "image"
},
"image-edit": {
instruction: "",
blocktype: "image"
}
}
const systemPrompts = {
"Challenge my thinking": {
prompt: `Your task is to interpret a screenshot of a whiteboard, translating its ideas into a Mermaid graph. The whiteboard will encompass thoughts on a subject. Within the mind map, distinguish ideas that challenge, dispute, or contradict the whiteboard content. Additionally, include concepts that expand, complement, or advance the user's thinking. Utilize the Mermaid graph diagram type and present the resulting Mermaid diagram within a code block. Ensure the Mermaid script excludes the use of parentheses ().`,
type: "mermaid",
help: "Translate your image and optional text prompt into a Mermaid mindmap. If there are conversion errors, edit the Mermaid script under 'More Tools'."
},
"Convert sketch to shapes": {
prompt: `Given an image featuring various geometric shapes drawn by the user, your objective is to analyze the input and generate SVG code that accurately represents these shapes. Your output will be the SVG code enclosed in an HTML code block.`,
type: "svg",
help: "Convert selected scribbles into shapes; works better with fewer shapes. Experimental and may not produce good drawings."
},
"Create a simple Excalidraw icon": {
prompt: `Given a description of an SVG image from the user, your objective is to generate the corresponding SVG code. Avoid incorporating textual elements within the generated SVG. Your output should be the resulting SVG code enclosed in an HTML code block.`,
type: "svg",
help: "Convert text prompts into simple icons inserted as Excalidraw elements. Expect only a text prompt. Experimental and may not produce good drawings."
},
"Edit an image": {
prompt: null,
type: "image-edit",
help: "Image elements will be used as the Image. Shapes on top of the image will be the Mask. Use the prompt to instruct Dall-e about the changes. Dall-e-2 model will be used."
},
"Generate an image from image and prompt": {
prompt: "Your task involves receiving an image and a textual prompt from the user. Your goal is to craft a detailed, accurate, and descriptive narrative of the image, tailored for effective image generation. Utilize the user-provided text prompt to inform and guide your depiction of the image. Ensure the resulting image remains text-free.",
type: "image-gen",
help: "Generate an image based on the drawing and prompt using ChatGPT-Vision and Dall-e. Provide a contextual text-prompt for accurate interpretation."
},
"Generate an image from prompt": {
prompt: null,
type: "image-gen",
help: "Send only the text prompt to OpenAI. Provide a detailed description; OpenAI will enrich your prompt automatically. To avoid it, start your prompt like this 'DO NOT add any detail, just use it AS-IS:'"
},
"Generate an image to illustrate a quote": {
prompt: "Your task involves transforming a user-provided quote into a detailed and imaginative illustration. Craft a visual representation that captures the essence of the quote and resonates well with a broad audience. If the Author's name is provided, aim to establish a connection between the illustration and the Author. This can be achieved by referencing a well-known story from the Author, situating the image in the Author's era or setting, or employing other creative methods of association. Additionally, provide preferences for styling, such as the chosen medium and artistic direction, to guide the image creation process. Ensure the resulting image remains text-free. Your task output should comprise a descriptive and detailed narrative aimed at facilitating the creation of a captivating illustration from the quote.",
type: "image-gen",
help: "ExcaliAI will create an image prompt to illustrate your text input - a quote - with GPT, then generate an image using Dall-e. In case you include the Author's name, GPT will try to generate an image that in some way references the Author."
},
"Visual brainstorm": {
prompt: "Your objective is to interpret a screenshot of a whiteboard, creating an image aimed at sparking further thoughts on the subject. The whiteboard will present diverse ideas about a specific topic. Your generated image should achieve one of two purposes: highlighting concepts that challenge, dispute, or contradict the whiteboard content, or introducing ideas that expand, complement, or enrich the user's thinking. You have the option to include multiple tiles in the resulting image, resembling a sequence akin to a comic strip. Ensure that the image remains devoid of text.",
type: "image-gen",
help: "Use ChatGPT Visions and Dall-e to create an image based on your text prompt and image to spark new ideas."
},
"Wireframe to code": {
prompt: `You are an expert tailwind developer. A user will provide you with a low-fidelity wireframe of an application and you will return a single html file that uses tailwind to create the website. Use creative license to make the application more fleshed out. Write the necessary javascript code. If you need to insert an image, use placehold.co to create a placeholder image.`,
type: "html",
help: "Use GPT Visions to interpret the wireframe and generate a web application. You may copy the resulting code from the active embeddable's top left menu."
},
}
const IMAGE_WARNING = "The generated image is linked through a temporary OpenAI URL and will be removed in approximately 30 minutes. To save it permanently, choose 'Save image from URL to local file' from the Obsidian Command Palette."
// --------------------------------------
// Initialize values and settings
// --------------------------------------
let settings = ea.getScriptSettings();
if(!settings["Agent's Task"]) {
settings = {
"Agent's Task": "Wireframe to code",
"User Prompt": "",
};
await ea.setScriptSettings(settings);
}
const OPENAI_API_KEY = ea.plugin.settings.openAIAPIToken;
if(!OPENAI_API_KEY || OPENAI_API_KEY === "") {
new Notice("You must first configure your API key in Excalidraw Plugin Settings");
return;
}
let userPrompt = settings["User Prompt"] ?? "";
let agentTask = settings["Agent's Task"];
let imageSize = settings["Image Size"]??"1024x1024";
if(!systemPrompts.hasOwnProperty(agentTask)) {
agentTask = Object.keys(systemPrompts)[0];
}
let imageModel, valideSizes;
const setImageModelAndSizes = () => {
imageModel = systemPrompts[agentTask].type === "image-edit"
? "dall-e-2"
: ea.plugin.settings.openAIDefaultImageGenerationModel;
validSizes = imageModel === "dall-e-2"
? [`256x256`, `512x512`, `1024x1024`]
: (imageModel === "dall-e-3"
? [`1024x1024`, `1792x1024`, `1024x1792`]
: [`1024x1024`])
if(!validSizes.includes(imageSize)) {
imageSize = "1024x1024";
dirty = true;
}
}
setImageModelAndSizes();
// --------------------------------------
// Generate Image Blob From Selected Excalidraw Elements
// --------------------------------------
const calculateImageScale = (elements) => {
const bb = ea.getBoundingBox(elements);
const size = (bb.width*bb.height);
const minRatio = Math.sqrt(360000/size);
const maxRatio = Math.sqrt(size/16000000);
return minRatio > 1
? minRatio
: (
maxRatio > 1
? 1/maxRatio
: 1
);
}
const createMask = async (dataURL) => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
// If opaque (alpha > 0), make it transparent
if (data[i + 3] > 0) {
data[i + 3] = 0; // Set alpha to 0 (transparent)
} else if (data[i + 3] === 0) {
// If fully transparent, make it red
data[i] = 255; // Red
data[i + 1] = 0; // Green
data[i + 2] = 0; // Blue
data[i + 3] = 255; // make it opaque
}
}
ctx.putImageData(imageData, 0, 0);
const maskDataURL = canvas.toDataURL();
resolve(maskDataURL);
};
img.onerror = error => {
reject(error);
};
img.src = dataURL;
});
}
//https://platform.openai.com/docs/api-reference/images/createEdit
//dall-e-2 image edit only works on square images
//if targetDalleImageEdit === true then the image and the mask will be returned in two separate dataURLs
let squareBB;
const generateCanvasDataURL = async (view, targetDalleImageEdit=false) => {
let PADDING = 5;
await view.forceSave(true); //to ensure recently embedded PNG and other images are saved to file
const viewElements = ea.getViewSelectedElements();
if(viewElements.length === 0) {
return {imageDataURL: null, maskDataURL: null} ;
}
ea.copyViewElementsToEAforEditing(viewElements, true); //copying the images objects over to EA for PNG generation
let maskDataURL;
const loader = ea.getEmbeddedFilesLoader(false);
let scale = calculateImageScale(ea.getElements());
const bb = ea.getBoundingBox(viewElements);
if(ea.getElements()
.filter(el=>el.type==="image")
.some(el=>Math.round(el.width) === Math.round(bb.width) && Math.round(el.height) === Math.round(bb.height))
) { PADDING = 0; }
let exportSettings = {withBackground: true, withTheme: true};
if(targetDalleImageEdit) {
PADDING = 0;
const strokeColor = ea.style.strokeColor;
const backgroundColor = ea.style.backgroundColor;
ea.style.backgroundColor = "transparent";
ea.style.strokeColor = "transparent";
let rectID;
if(bb.height > bb.width) {
rectID = ea.addRect(bb.topX-(bb.height-bb.width)/2, bb.topY,bb.height, bb.height);
}
if(bb.width > bb.height) {
rectID = ea.addRect(bb.topX, bb.topY-(bb.width-bb.height)/2,bb.width, bb.width);
}
if(bb.height === bb.width) {
rectID = ea.addRect(bb.topX, bb.topY, bb.width, bb.height);
}
const rect = ea.getElement(rectID);
squareBB = {topX: rect.x-PADDING, topY: rect.y-PADDING, width: rect.width + 2*PADDING, height: rect.height + 2*PADDING};
ea.style.strokeColor = strokeColor;
ea.style.backgroundColor = backgroundColor;
ea.getElements().filter(el=>el.type === "image").forEach(el=>{el.isDeleted = true});
dalleWidth = parseInt(imageSize.split("x")[0]);
scale = dalleWidth/squareBB.width;
exportSettings = {withBackground: false, withTheme: true};
maskDataURL= await ea.createPNGBase64(
null, scale, exportSettings, loader, "light", PADDING
);
maskDataURL = await createMask(maskDataURL)
ea.getElements().filter(el=>el.type === "image").forEach(el=>{el.isDeleted = false});
ea.getElements().filter(el=>el.type !== "image" && el.id !== rectID).forEach(el=>{el.isDeleted = true});
}
const imageDataURL = await ea.createPNGBase64(
null, scale, exportSettings, loader, "light", PADDING
);
ea.clear();
return {imageDataURL, maskDataURL};
}
let {imageDataURL, maskDataURL} = await generateCanvasDataURL(ea.targetView, systemPrompts[agentTask].type === "image-edit");
// --------------------------------------
// Support functions - embeddable spinner and error
// --------------------------------------
const spinner = await ea.convertStringToDataURL(`
<html><head><style>
html, body {width: 100%; height: 100%; color: ${ea.getExcalidrawAPI().getAppState().theme === "dark" ? "white" : "black"};}
body {display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 1rem; overflow: hidden;}
.Spinner {display: flex; align-items: center; justify-content: center; margin-left: auto; margin-right: auto;}
.Spinner svg {animation: rotate 1.6s linear infinite; transform-origin: center center; width: 40px; height: 40px;}
.Spinner circle {stroke: currentColor; animation: dash 1.6s linear 0s infinite; stroke-linecap: round;}
@keyframes rotate {100% {transform: rotate(360deg);}}
@keyframes dash {
0% {stroke-dasharray: 1, 300; stroke-dashoffset: 0;}
50% {stroke-dasharray: 150, 300; stroke-dashoffset: -200;}
100% {stroke-dasharray: 1, 300; stroke-dashoffset: -280;}
}
</style></head><body>
<div class="Spinner">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="46" stroke-width="8" fill="none" stroke-miter-limit="10"/>
</svg>
</div>
<div>Generating...</div>
</body></html>`);
const errorMessage = async (spinnerID, message) => {
const error = "Something went wrong! Check developer console for more.";
const details = message ? `<p>${message}</p>` : "";
const errorDataURL = await ea.convertStringToDataURL(`
<html><head><style>
html, body {height: 100%;}
body {display: flex; flex-direction: column; align-items: center; justify-content: center; color: red;}
h1, h3 {margin-top: 0;margin-bottom: 0.5rem;}
</style></head><body>
<h1>Error!</h1>
<h3>${error}</h3>${details}
</body></html>`);
new Notice (error);
ea.getElement(spinnerID).link = errorDataURL;
ea.addElementsToView(false,true);
}
// --------------------------------------
// Utility to write Mermaid to dialog
// --------------------------------------
const EDITOR_LS_KEYS = {
OAI_API_KEY: "excalidraw-oai-api-key",
MERMAID_TO_EXCALIDRAW: "mermaid-to-excalidraw",
PUBLISH_LIBRARY: "publish-library-data",
};
const setMermaidDataToStorage = (mermaidDefinition) => {
try {
window.localStorage.setItem(
EDITOR_LS_KEYS.MERMAID_TO_EXCALIDRAW,
JSON.stringify(mermaidDefinition)
);
return true;
} catch (error) {
console.warn(`localStorage.setItem error: ${error.message}`);
return false;
}
};
// --------------------------------------
// Submit Prompt
// --------------------------------------
const generateImage = async(text, spinnerID, bb) => {
const requestObject = {
text,
imageGenerationProperties: {
size: imageSize,
//quality: "standard", //not supported by dall-e-2
n:1,
},
};
const result = await ea.postOpenAI(requestObject);
console.log({result, json:result?.json});
if(!result?.json?.data?.[0]?.url) {
await errorMessage(spinnerID, result?.json?.error?.message);
return;
}
const spinner = ea.getElement(spinnerID)
spinner.isDeleted = true;
const imageID = await ea.addImage(spinner.x, spinner.y, result.json.data[0].url);
const imageEl = ea.getElement(imageID);
const revisedPrompt = result.json.data[0].revised_prompt;
if(revisedPrompt) {
ea.style.fontSize = 16;
const rectID = ea.addText(imageEl.x+15, imageEl.y + imageEl.height + 50, revisedPrompt, {
width: imageEl.width-30,
textAlign: "center",
textVerticalAlign: "top",
box: true,
})
ea.getElement(rectID).strokeColor = "transparent";
ea.getElement(rectID).backgroundColor = "transparent";
ea.addToGroup(ea.getElements().filter(el=>el.id !== spinnerID).map(el=>el.id));
}
await ea.addElementsToView(false, true, true);
ea.getExcalidrawAPI().setToast({
message: IMAGE_WARNING,
duration: 15000,
closable: true
});
}
const run = async (text) => {
if(!text && !imageDataURL) {
new Notice("No prompt, aborting");
return;
}
const systemPrompt = systemPrompts[agentTask];
const outputType = outputTypes[systemPrompt.type];
const isImageGenRequest = outputType.blocktype === "image";
const isImageEditRequest = systemPrompt.type === "image-edit";
if(isImageEditRequest) {
if(!text) {
new Notice("You must provide a text prompt with instructions for how the image should be modified");
return;
}
if(!imageDataURL || !maskDataURL) {
new Notice("You must provide an image and a mask");
return;
}
}
//place spinner next to selected elements
const bb = ea.getBoundingBox(ea.getViewSelectedElements());
const spinnerID = ea.addEmbeddable(bb.topX+bb.width+100,bb.topY-(720-bb.height)/2,550,720,spinner);
//this block is in an async call using the isEACompleted flag because otherwise during debug Obsidian
//goes black (not freezes, but does not get a new frame for some reason)
//palcing this in an async call solves this issue
//If you know why this is happening and can offer a better solution, please reach out to @zsviczian
let isEACompleted = false;
setTimeout(async()=>{
await ea.addElementsToView(false,true);
ea.clear();
const embeddable = ea.getViewElements().filter(el=>el.id===spinnerID);
ea.copyViewElementsToEAforEditing(embeddable);
const els = ea.getViewSelectedElements();
ea.viewZoomToElements(false, els.concat(embeddable));
isEACompleted = true;
});
if(isImageGenRequest && !systemPrompt.prompt && !isImageEditRequest) {
generateImage(text,spinnerID,bb);
return;
}
const requestObject = isImageEditRequest
? {
...imageDataURL ? {image: imageDataURL} : {},
...(text && text.trim() !== "") ? {text} : {},
imageGenerationProperties: {
size: imageSize,
//quality: "standard", //not supported by dall-e-2
n:1,
mask: maskDataURL,
},
}
: {
...imageDataURL ? {image: imageDataURL} : {},
...(text && text.trim() !== "") ? {text} : {},
systemPrompt: systemPrompt.prompt,
instruction: outputType.instruction,
}
//Get result from GPT
const result = await ea.postOpenAI(requestObject);
console.log({result, json:result?.json});
//checking that EA has completed. Because the postOpenAI call is an async await
//I don't expect EA not to be completed by now. However the devil never sleeps.
//This (the insomnia of the Devil) is why I have a watchdog here as well
let counter = 0
while(!isEACompleted && counter++<10) sleep(50);
if(!isEACompleted) {
await errorMessage(spinnerID, "Unexpected issue with ExcalidrawAutomate");
return;
}
if(isImageEditRequest) {
if(!result?.json?.data?.[0]?.url) {
await errorMessage(spinnerID, result?.json?.error?.message);
return;
}
const spinner = ea.getElement(spinnerID)
spinner.isDeleted = true;
const imageID = await ea.addImage(spinner.x, spinner.y, result.json.data[0].url);
await ea.addElementsToView(false, true, true);
ea.getExcalidrawAPI().setToast({
message: IMAGE_WARNING,
duration: 15000,
closable: true
});
return;
}
if(!result?.json?.hasOwnProperty("choices")) {
await errorMessage(spinnerID, result?.json?.error?.message);
return;
}
//extract codeblock and display result
let content = ea.extractCodeBlocks(result.json.choices[0]?.message?.content)[0]?.data;
if(!content) {
await errorMessage(spinnerID);
return;
}
if(isImageGenRequest) {
generateImage(content,spinnerID,bb);
return;
}
switch(outputType.blocktype) {
case "html":
ea.getElement(spinnerID).link = await ea.convertStringToDataURL(content);
ea.addElementsToView(false,true);
break;
case "svg":
ea.getElement(spinnerID).isDeleted = true;
ea.importSVG(content);
ea.addToGroup(ea.getElements().map(el=>el.id));
if(ea.getViewSelectedElements().length>0) {
ea.targetView.currentPosition = {x: bb.topX+bb.width+100, y: bb.topY};
}
ea.addElementsToView(true, false);
break;
case "mermaid":
if(content.startsWith("mermaid")) {
content = content.replace(/^mermaid/,"").trim();
}
try {
result = await ea.addMermaid(content);
if(typeof result === "string") {
await errorMessage(spinnerID, "Open [More Tools / Mermaid to Excalidraw] to manually fix the received mermaid script<br><br>" + result);
return;
}
} catch (e) {
ea.addText(0,0,content);
}
ea.getElement(spinnerID).isDeleted = true;
ea.targetView.currentPosition = {x: bb.topX+bb.width+100, y: bb.topY-bb.height};
await ea.addElementsToView(true, false);
setMermaidDataToStorage(content);
new Notice("Open More Tools/Mermaid to Excalidraw in the top tools menu to edit the generated diagram",8000);
break;
}
}
// --------------------------------------
// User Interface
// --------------------------------------
let previewDiv;
const fragWithHTML = (html) => createFragment((frag) => (frag.createDiv().innerHTML = html));
const isImageGenerationTask = () => systemPrompts[agentTask].type === "image-gen" || systemPrompts[agentTask].type === "image-edit";
const addPreviewImage = () => {
if(!previewDiv) return;
previewDiv.empty();
previewDiv.createEl("img",{
attr: {
style: `max-width: 100%;max-height: 30vh;`,
src: imageDataURL,
}
});
if(maskDataURL) {
previewDiv.createEl("img",{
attr: {
style: `max-width: 100%;max-height: 30vh;`,
src: maskDataURL,
}
});
}
}
const configModal = new ea.obsidian.Modal(app);
configModal.modalEl.style.width="100%";
configModal.modalEl.style.maxWidth="1000px";
configModal.onOpen = async () => {
const contentEl = configModal.contentEl;
contentEl.createEl("h1", {text: "ExcaliAI"});
let systemPromptTextArea, systemPromptDiv, imageSizeSetting, imageSizeSettingDropdown, helpEl;
new ea.obsidian.Setting(contentEl)
.setName("What would you like to do?")
.addDropdown(dropdown=>{
Object.keys(systemPrompts).forEach(key=>dropdown.addOption(key,key));
dropdown
.setValue(agentTask)
.onChange(async (value) => {
dirty = true;
const prevTask = agentTask;
agentTask = value;
if(
(systemPrompts[prevTask].type === "image-edit" && systemPrompts[value].type !== "image-edit") ||
(systemPrompts[prevTask].type !== "image-edit" && systemPrompts[value].type === "image-edit")
) {
({imageDataURL, maskDataURL} = await generateCanvasDataURL(ea.targetView, systemPrompts[value].type === "image-edit"));
addPreviewImage();
setImageModelAndSizes();
while (imageSizeSettingDropdown.selectEl.options.length > 0) { imageSizeSettingDropdown.selectEl.remove(0); }
validSizes.forEach(size=>imageSizeSettingDropdown.addOption(size,size));
imageSizeSettingDropdown.setValue(imageSize);
}
imageSizeSetting.settingEl.style.display = isImageGenerationTask() ? "" : "none";
const prompt = systemPrompts[value].prompt;
helpEl.innerHTML = `<b>Help: </b>` + systemPrompts[value].help;
if(prompt) {
systemPromptDiv.style.display = "";
systemPromptTextArea.setValue(systemPrompts[value].prompt);
} else {
systemPromptDiv.style.display = "none";
}
});
})
helpEl = contentEl.createEl("p");
helpEl.innerHTML = `<b>Help: </b>` + systemPrompts[agentTask].help;
systemPromptDiv = contentEl.createDiv();
systemPromptDiv.createEl("h4", {text: "Customize System Prompt"});
systemPromptDiv.createEl("span", {text: "Unless you know what you are doing I do not recommend changing the system prompt"})
const systemPromptSetting = new ea.obsidian.Setting(systemPromptDiv)
.addTextArea(text => {
systemPromptTextArea = text;
const prompt = systemPrompts[agentTask].prompt;
text.inputEl.style.minHeight = "10em";
text.inputEl.style.width = "100%";
text.setValue(prompt);
text.onChange(value => {
systemPrompts[value].prompt = value;
});
if(!prompt) systemPromptDiv.style.display = "none";
})
systemPromptSetting.nameEl.style.display = "none";
systemPromptSetting.descEl.style.display = "none";
systemPromptSetting.infoEl.style.display = "none";
contentEl.createEl("h4", {text: "User Prompt"});
const userPromptSetting = new ea.obsidian.Setting(contentEl)
.addTextArea(text => {
text.inputEl.style.minHeight = "10em";
text.inputEl.style.width = "100%";
text.setValue(userPrompt);
text.onChange(value => {
userPrompt = value;
dirty = true;
})
})
userPromptSetting.nameEl.style.display = "none";
userPromptSetting.descEl.style.display = "none";
userPromptSetting.infoEl.style.display = "none";
imageSizeSetting = new ea.obsidian.Setting(contentEl)
.setName("Select image size")
.setDesc(fragWithHTML("<mark>⚠️ Important ⚠️</mark>: " + IMAGE_WARNING))
.addDropdown(dropdown=>{
validSizes.forEach(size=>dropdown.addOption(size,size));
imageSizeSettingDropdown = dropdown;
dropdown
.setValue(imageSize)
.onChange(async (value) => {
dirty = true;
imageSize = value;
if(systemPrompts[agentTask].type === "image-edit") {
({imageDataURL, maskDataURL} = await generateCanvasDataURL(ea.targetView, true));
addPreviewImage();
}
});
})
imageSizeSetting.settingEl.style.display = isImageGenerationTask() ? "" : "none";
if(imageDataURL) {
previewDiv = contentEl.createDiv({
attr: {
style: "text-align: center;",
}
});
addPreviewImage();
} else {
contentEl.createEl("h4", {text: "No elements are selected from your canvas"});
contentEl.createEl("span", {text: "Because there are no Excalidraw elements selected on the canvas, only the text prompt will be sent to OpenAI."});
}
new ea.obsidian.Setting(contentEl)
.addButton(button =>
button
.setButtonText("Run")
.onClick((event)=>{
run(userPrompt); //Obsidian crashes otherwise, likely has to do with requesting an new frame for react
configModal.close();
})
);
}
configModal.onClose = () => {
if(dirty) {
settings["User Prompt"] = userPrompt;
settings["Agent's Task"] = agentTask;
settings["Image Size"] = imageSize;
ea.setScriptSettings(settings);
}
}
configModal.open();

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path d="M320 0c17.7 0 32 14.3 32 32V96H472c39.8 0 72 32.2 72 72V440c0 39.8-32.2 72-72 72H168c-39.8 0-72-32.2-72-72V168c0-39.8 32.2-72 72-72H288V32c0-17.7 14.3-32 32-32zM208 384c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H208zm96 0c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H304zm96 0c-8.8 0-16 7.2-16 16s7.2 16 16 16h32c8.8 0 16-7.2 16-16s-7.2-16-16-16H400zM264 256a40 40 0 1 0 -80 0 40 40 0 1 0 80 0zm152 40a40 40 0 1 0 0-80 40 40 0 1 0 0 80zM48 224H64V416H48c-26.5 0-48-21.5-48-48V272c0-26.5 21.5-48 48-48zm544 0c26.5 0 48 21.5 48 48v96c0 26.5-21.5 48-48 48H576V224h16z"/></svg>

After

Width:  |  Height:  |  Size: 694 B

676
ea-scripts/Golden Ratio.md Normal file
View File

@@ -0,0 +1,676 @@
/*
The script performs two different functions depending on the elements selected in the view.
1) In case you select text elements, the script will cycle through a set of font scales. First the 2 larger fonts following the Fibonacci sequence (fontsize * φ; fonsize * φ^2), then the 2 smaller fonts (fontsize / φ; fontsize / φ^2), finally the original size, followed again by the 2 larger fonts. If you wait 2 seconds, the sequence clears and starts from which ever font size you are on. So if you want the 3rd larges font, then toggle twice, wait 2 sec, then toggle again.
2) In case you select a single rectangle, the script will open the "Golden Grid", "Golden Spiral" window, where you can set up the type of grid or spiral you want to insert into the document.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/golden-ratio.jpg)
<iframe width="560" height="315" src="https://www.youtube.com/embed/2SHn_ruax-s" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
Gravitational point of spiral: $$\left[x,y\right]=\left[ x + \frac{{\text{width} \cdot \phi^2}}{{\phi^2 + 1}}\;, \; y + \frac{{\text{height} \cdot \phi^2}}{{\phi^2 + 1}} \right]$$
Dimensions of inner rectangles in case of Double Spiral: $$[width, height] = \left[\frac{width\cdot(\phi^2+1)}{2\phi^2}\;, \;\frac{height\cdot(\phi^2+1)}{2\phi^2}\right]$$
```js*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.4.0")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
const phi = (1 + Math.sqrt(5)) / 2; // Golden Ratio (φ)
const inversePhi = (1-1/phi);
const pointsPerCurve = 20; // Number of points per curve segment
const ownerWindow = ea.targetView.ownerWindow;
const hostLeaf = ea.targetView.leaf;
let dirty = false;
const ids = [];
const textEls = ea.getViewSelectedElements().filter(el=>el.type === "text");
let rect = ea.getViewSelectedElements().length === 1 ? ea.getViewSelectedElement() : null;
if(!rect || rect.type !== "rectangle") {
//Fontsize cycle
if(textEls.length>0) {
if(window.excalidrawGoldenRatio) {
clearTimeout(window.excalidrawGoldenRatio?.timer);
} else {
window.excalidrawGoldenRatio = {timer: null, cycle:-1};
}
window.excalidrawGoldenRatio.timer = setTimeout(()=>{delete window.excalidrawGoldenRatio;},2000);
window.excalidrawGoldenRatio.cycle = (window.excalidrawGoldenRatio.cycle+1)%5;
ea.copyViewElementsToEAforEditing(textEls);
ea.getElements().forEach(el=> {
el.fontSize = window.excalidrawGoldenRatio.cycle === 2
? el.fontSize / Math.pow(phi,4)
: el.fontSize * phi;
ea.style.fontFamily = el.fontFamily;
ea.style.fontSize = el.fontSize;
const {width, height } = ea.measureText(el.originalText);
el.width = width;
el.height = height;
});
ea.addElementsToView();
return;
}
new Notice("Select text elements, or a select a single rectangle");
return;
}
ea.copyViewElementsToEAforEditing([rect]);
rect = ea.getElement(rect.id);
ea.style.strokeColor = rect.strokeColor;
ea.style.strokeWidth = rect.strokeWidth;
ea.style.roughness = rect.roughness;
ea.style.angle = rect.angle;
let {x,y,width,height} = rect;
// --------------------------------------------
// Load Settings
// --------------------------------------------
let settings = ea.getScriptSettings();
if(!settings["Horizontal Grid"]) {
settings = {
"Horizontal Grid" : {
value: "left-right",
valueset: ["none","letf-right","right-left","center-out","center-in"]
},
"Vertical Grid": {
value: "none",
valueset: ["none","top-down","bottom-up","center-out","center-in"]
},
"Size": {
value: "6",
valueset: ["2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20"]
},
"Aspect Choice": {
value: "none",
valueset: ["none","adjust-width","adjust-height"]
},
"Type": "grid",
"Spiral Orientation": {
value: "top-left",
valueset: ["double","top-left","top-right","bottom-right","bottom-left"]
},
"Lock Elements": false,
"Send to Back": false,
"Update Style": false,
"Bold Spiral": false,
};
await ea.setScriptSettings(settings);
}
let hDirection = settings["Horizontal Grid"].value;
let vDirection = settings["Vertical Grid"].value;
let aspectChoice = settings["Aspect Choice"].value;
let type = settings["Type"];
let spiralOrientation = settings["Spiral Orientation"].value;
let lockElements = settings["Lock Elements"];
let sendToBack = settings["Send to Back"];
let size = parseInt(settings["Size"].value);
let updateStyle = settings["Update Style"];
let boldSpiral = settings["Bold Spiral"];
// --------------------------------------------
// Rotation
// --------------------------------------------
let centerX, centerY;
const rotatePointAndAddToElementList = (elementID) => {
ids.push(elementID);
const line = ea.getElement(elementID);
// Calculate the initial position of the line's center
const lineCenterX = line.x + line.width / 2;
const lineCenterY = line.y + line.height / 2;
// Calculate the difference between the line's center and the rectangle's center
const diffX = lineCenterX - (rect.x + rect.width / 2);
const diffY = lineCenterY - (rect.y + rect.height / 2);
// Apply the rotation to the difference
const cosTheta = Math.cos(rect.angle);
const sinTheta = Math.sin(rect.angle);
const rotatedX = diffX * cosTheta - diffY * sinTheta;
const rotatedY = diffX * sinTheta + diffY * cosTheta;
// Calculate the new position of the line's center with respect to the rectangle's center
const newLineCenterX = rotatedX + (rect.x + rect.width / 2);
const newLineCenterY = rotatedY + (rect.y + rect.height / 2);
// Update the line's coordinates by adjusting for the change in the center
line.x += newLineCenterX - lineCenterX;
line.y += newLineCenterY - lineCenterY;
}
const rotatePointsWithinRectangle = (points) => {
const centerX = rect.x + rect.width / 2;
const centerY = rect.y + rect.height / 2;
const cosTheta = Math.cos(rect.angle);
const sinTheta = Math.sin(rect.angle);
const rotatedPoints = points.map(([x, y]) => {
// Translate the point relative to the rectangle's center
const translatedX = x - centerX;
const translatedY = y - centerY;
// Apply the rotation to the translated coordinates
const rotatedX = translatedX * cosTheta - translatedY * sinTheta;
const rotatedY = translatedX * sinTheta + translatedY * cosTheta;
// Translate back to the original coordinate system
const finalX = rotatedX + centerX;
const finalY = rotatedY + centerY;
return [finalX, finalY];
});
return rotatedPoints;
}
// --------------------------------------------
// Grid
// --------------------------------------------
const calculateGoldenSum = (baseOfGoldenGrid, pow) => {
const ratio = 1 / phi;
const geometricSum = baseOfGoldenGrid * ((1 - Math.pow(ratio, pow)) / (1 - ratio));
return geometricSum;
};
const findBaseForGoldenGrid = (targetValue, n, scenario) => {
const ratio = 1 / phi;
if (scenario === "center-out") {
return targetValue * (2-2*ratio) / (1 + ratio + 2*Math.pow(ratio,n));
} else if (scenario === "center-in") {
return targetValue*2*(1-ratio)*Math.pow(phi,n-1) /(2*Math.pow(phi,n-1)*(1-Math.pow(ratio,n))-1+ratio);
} else {
return targetValue * (1-ratio)/(1-Math.pow(ratio,n));
}
}
const calculateOffsetVertical = (scenario, base) => {
if (scenario === "center-out") return base / 2;
if (scenario === "center-in") return base / Math.pow(phi, size + 1) / 2;
return 0;
};
const horizontal = (direction, scenario) => {
const base = findBaseForGoldenGrid(width, size + 1, scenario);
const totalGridWidth = calculateGoldenSum(base, size + 1);
for (i = 1; i <= size; i++) {
const offset =
scenario === "center-out"
? totalGridWidth - calculateGoldenSum(base, i)
: calculateGoldenSum(base, size + 1 - i);
const x2 =
direction === "left"
? x + offset
: x + width - offset;
rotatePointAndAddToElementList(
ea.addLine([
[x2, y],
[x2, y + height],
])
);
}
};
const vertical = (direction, scenario) => {
const base = findBaseForGoldenGrid(height, size + 1, scenario);
const totalGridWidth = calculateGoldenSum(base, size + 1);
for (i = 1; i <= size; i++) {
const offset =
scenario === "center-out"
? totalGridWidth - calculateGoldenSum(base, i)
: calculateGoldenSum(base, size + 1 - i);
const y2 =
direction === "top"
? y + offset
: y + height - offset;
rotatePointAndAddToElementList(
ea.addLine([
[x, y2],
[x+width, y2],
])
);
}
};
const centerHorizontal = (scenario) => {
width = width / 2;
horizontal("left", scenario);
x += width;
horizontal("right", scenario);
x -= width;
width = 2*width;
};
const centerVertical = (scenario) => {
height = height / 2;
vertical("top", scenario);
y += height;
vertical("bottom", scenario);
y -= height;
height = 2*height;
};
const drawGrid = () => {
switch(hDirection) {
case "none": break;
case "left-right": horizontal("left"); break;
case "right-left": horizontal("right"); break;
case "center-out": centerHorizontal("center-out"); break;
case "center-in": centerHorizontal("center-in"); break;
}
switch(vDirection) {
case "none": break;
case "top-down": vertical("top"); break;
case "bottom-up": vertical("bottom"); break;
case "center-out": centerVertical("center-out"); break;
case "center-in": centerVertical("center-in"); break;
}
}
// --------------------------------------------
// Draw Spiral
// --------------------------------------------
const drawSpiral = () => {
let nextX, nextY, nextW, nextH;
let spiralPoints = [];
let curveEndX, curveEndY, curveX, curveY;
const phaseShift = {
"bottom-right": 0,
"bottom-left": 2,
"top-left": 2,
"top-right": 0,
}[spiralOrientation];
let curveStartX = {
"bottom-right": x,
"bottom-left": x+width,
"top-left": x+width,
"top-right": x,
}[spiralOrientation];
let curveStartY = {
"bottom-right": y+height,
"bottom-left": y+height,
"top-left": y,
"top-right": y,
}[spiralOrientation];
const mirror = spiralOrientation === "bottom-left" || spiralOrientation === "top-right";
for (let i = phaseShift; i < size+phaseShift; i++) {
const curvePhase = i%4;
const linePhase = mirror?[0,3,2,1][curvePhase]:curvePhase;
const longHorizontal = width/phi;
const shortHorizontal = width*inversePhi;
const longVertical = height/phi;
const shortVertical = height*inversePhi;
switch(linePhase) {
case 0: //right
nextX = x + longHorizontal;
nextY = y;
nextW = shortHorizontal;
nextH = height;
break;
case 1: //down
nextX = x;
nextY = y + longVertical;
nextW = width;
nextH = shortVertical;
break;
case 2: //left
nextX = x;
nextY = y;
nextW = shortHorizontal;
nextH = height;
break;
case 3: //up
nextX = x;
nextY = y;
nextW = width;
nextH = shortVertical;
break;
}
switch(curvePhase) {
case 0: //right
curveEndX = nextX;
curveEndY = mirror ? nextY + nextH : nextY;
break;
case 1: //down
curveEndX = nextX + nextW;
curveEndY = mirror ? nextY + nextH : nextY;
break;
case 2: //left
curveEndX = nextX + nextW;
curveEndY = mirror ? nextY : nextY + nextH;
break;
case 3: //up
curveEndX = nextX;
curveEndY = mirror ? nextY : nextY + nextH;
break;
}
// Add points for the curve segment
for (let j = 0; j <= pointsPerCurve; j++) {
const t = j / pointsPerCurve;
const angle = -Math.PI / 2 * t;
switch(curvePhase) {
case 0:
curveX = curveEndX + (curveStartX - curveEndX) * Math.cos(angle);
curveY = curveStartY + (curveStartY - curveEndY) * Math.sin(angle);
break;
case 1:
curveX = curveStartX + (curveStartX - curveEndX) * Math.sin(angle);
curveY = curveEndY + (curveStartY - curveEndY) * Math.cos(angle);
break;
case 2:
curveX = curveEndX + (curveStartX - curveEndX) * Math.cos(angle);
curveY = curveStartY + (curveStartY - curveEndY) * Math.sin(angle);
break;
case 3:
curveX = curveStartX + (curveStartX - curveEndX) * Math.sin(angle);
curveY = curveEndY + (curveStartY - curveEndY) * Math.cos(angle);
break;
}
spiralPoints.push([curveX, curveY]);
}
x = nextX;
y = nextY;
curveStartX = curveEndX;
curveStartY = curveEndY;
width = nextW;
height = nextH;
switch(linePhase) {
case 0: rotatePointAndAddToElementList(ea.addLine([[x,y],[x,y+height]]));break;
case 1: rotatePointAndAddToElementList(ea.addLine([[x,y],[x+width,y]]));break;
case 2: rotatePointAndAddToElementList(ea.addLine([[x+width,y],[x+width,y+height]]));break;
case 3: rotatePointAndAddToElementList(ea.addLine([[x,y+height],[x+width,y+height]]));break;
}
}
const strokeWidth = ea.style.strokeWidth;
ea.style.strokeWidth = strokeWidth * (boldSpiral ? 3 : 1);
const angle = ea.style.angle;
ea.style.angle = 0;
ids.push(ea.addLine(rotatePointsWithinRectangle(spiralPoints)));
ea.style.angle = angle;
ea.style.strokeWidth = strokeWidth;
}
// --------------------------------------------
// Update Aspect Ratio
// --------------------------------------------
const updateAspectRatio = () => {
switch(aspectChoice) {
case "none": break;
case "adjust-width": rect.width = rect.height/phi; break;
case "adjust-height": rect.height = rect.width/phi; break;
}
({x,y,width,height} = rect);
centerX = x + width/2;
centerY = y + height/2;
}
// --------------------------------------------
// UI
// --------------------------------------------
draw = async () => {
if(updateStyle) {
ea.style.strokeWidth = 0.5; rect.strokeWidth;
ea.style.roughness = 0; rect.roughness;
ea.style.roundness = null;
rect.strokeWidth = 0.5;
rect.roughness = 0;
rect.roundness = null;
}
updateAspectRatio();
switch(type) {
case "grid": drawGrid(); break;
case "spiral":
if(spiralOrientation === "double") {
wInner = width * (Math.pow(phi,2)+1)/(2*Math.pow(phi,2));
hInner = height * (Math.pow(phi,2)+1)/(2*Math.pow(phi,2));
x2 = width - wInner + x;
y2 = height - hInner + y;
width = wInner;
height = hInner;
rotatePointAndAddToElementList(ea.addRect(x,y,width,height));
spiralOrientation = "bottom-right";
drawSpiral();
x = x2;
y = y2;
width = wInner;
height = hInner;
rotatePointAndAddToElementList(ea.addRect(x,y,width,height));
spiralOrientation = "top-left";
drawSpiral();
spiralOrientation = "double";
} else {
drawSpiral();
}
break;
}
ea.addToGroup(ids);
ids.push(rect.id);
ea.addToGroup(ids);
lockElements && ea.getElements().forEach(el=>{el.locked = true;});
await ea.addElementsToView(false,false,!sendToBack);
!lockElements && ea.selectElementsInView(ea.getViewElements().filter(el => ids.includes(el.id)));
}
const modal = new ea.obsidian.Modal(app);
const fragWithHTML = (html) => createFragment((frag) => (frag.createDiv().innerHTML = html));
const keydownListener = (e) => {
if(hostLeaf !== app.workspace.activeLeaf) return;
if(hostLeaf.width === 0 && hostLeaf.height === 0) return;
if(e.key === "Enter" && (e.ctrlKey || e.shiftKey || e.metaKey || e.altKey)) {
e.preventDefault();
modal.close();
draw()
}
}
ownerWindow.addEventListener('keydown',keydownListener);
modal.onOpen = async () => {
const contentEl = modal.contentEl;
contentEl.createEl("h1", {text: "Golden Ratio"});
new ea.obsidian.Setting(contentEl)
.setName("Adjust Rectangle Aspect Ratio to Golden Ratio")
.addDropdown(dropdown=>dropdown
.addOption("none","None")
.addOption("adjust-width","Adjust Width")
.addOption("adjust-height","Adjust Height")
.setValue(aspectChoice)
.onChange(value => {
aspectChoice = value;
dirty = true;
})
);
new ea.obsidian.Setting(contentEl)
.setName("Change Line Style To: thin, architect, sharp")
.addToggle(toggle=>
toggle
.setValue(updateStyle)
.onChange(value => {
dirty = true;
updateStyle = value;
})
)
let sizeEl;
new ea.obsidian.Setting(contentEl)
.setName("Number of lines")
.addSlider(slider => slider
.setLimits(2, 20, 1)
.setValue(size)
.onChange(value => {
sizeEl.innerText = ` ${value.toString()}`;
size = value;
dirty = true;
}),
)
.settingEl.createDiv("", el => {
sizeEl = el;
el.style.minWidth = "2.3em";
el.style.textAlign = "right";
el.innerText = ` ${size.toString()}`;
});
new ea.obsidian.Setting(contentEl)
.setName("Lock Rectangle and Gridlines")
.addToggle(toggle=>
toggle
.setValue(lockElements)
.onChange(value => {
dirty = true;
lockElements = value;
})
)
new ea.obsidian.Setting(contentEl)
.setName("Send to Back")
.addToggle(toggle=>
toggle
.setValue(sendToBack)
.onChange(value => {
dirty = true;
sendToBack = value;
})
)
let bGrid, bSpiral;
let sHGrid, sVGrid, sSpiral, sBoldSpiral;
const showGridSettings = (value) => {
value
? (bGrid.setCta(), bSpiral.removeCta())
: (bGrid.removeCta(), bSpiral.setCta());
sHGrid.settingEl.style.display = value ? "" : "none";
sVGrid.settingEl.style.display = value ? "" : "none";
sSpiral.settingEl.style.display = !value ? "" : "none";
sBoldSpiral.settingEl.style.display = !value ? "" : "none";
}
new ea.obsidian.Setting(contentEl)
.setName(fragWithHTML("<h3>Output Type</h3>"))
.addButton(button => {
bGrid = button;
button
.setButtonText("Grid")
.setCta(type === "grid")
.onClick(event => {
type = "grid";
showGridSettings(true);
dirty = true;
})
})
.addButton(button => {
bSpiral = button;
button
.setButtonText("Spiral")
.setCta(type === "spiral")
.onClick(event => {
type = "spiral";
showGridSettings(false);
dirty = true;
})
});
sSpiral = new ea.obsidian.Setting(contentEl)
.setName("Spiral Orientation")
.addDropdown(dropdown=>dropdown
.addOption("double","Double")
.addOption("top-left","Top left")
.addOption("top-right","Top right")
.addOption("bottom-right","Bottom right")
.addOption("bottom-left","Bottom left")
.setValue(spiralOrientation)
.onChange(value => {
spiralOrientation = value;
dirty = true;
})
);
sBoldSpiral = new ea.obsidian.Setting(contentEl)
.setName("Spiral with Bold Line")
.addToggle(toggle=>
toggle
.setValue(boldSpiral)
.onChange(value => {
dirty = true;
boldSpiral = value;
})
)
sHGrid = new ea.obsidian.Setting(contentEl)
.setName("Horizontal Grid")
.addDropdown(dropdown=>dropdown
.addOption("none","None")
.addOption("left-right","Left to right")
.addOption("right-left","Right to left")
.addOption("center-out","Center out")
.addOption("center-in","Center in")
.setValue(hDirection)
.onChange(value => {
hDirection = value;
dirty = true;
})
);
sVGrid = new ea.obsidian.Setting(contentEl)
.setName("Vertical Grid")
.addDropdown(dropdown=>dropdown
.addOption("none","None")
.addOption("top-down","Top down")
.addOption("bottom-up","Bootom up")
.addOption("center-out","Center out")
.addOption("center-in","Center in")
.setValue(vDirection)
.onChange(value => {
vDirection = value;
dirty = true;
})
);
showGridSettings(type === "grid");
new ea.obsidian.Setting(contentEl)
.addButton(button => button
.setButtonText("Run")
.setCta(true)
.onClick(async (event) => {
draw();
modal.close();
})
);
}
modal.onClose = () => {
if(dirty) {
settings["Horizontal Grid"].value = hDirection;
settings["Vertical Grid"].value = vDirection;
settings["Size"].value = size.toString();
settings["Aspect Choice"].value = aspectChoice;
settings["Type"] = type;
settings["Spiral Orientation"].value = spiralOrientation;
settings["Lock Elements"] = lockElements;
settings["Send to Back"] = sendToBack;
settings["Update Style"] = updateStyle;
settings["Bold Spiral"] = boldSpiral;
ea.setScriptSettings(settings);
}
ownerWindow.removeEventListener('keydown',keydownListener);
}
modal.open();

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 642.8 373.7" stroke="currentColor"><path stroke-linecap="round" stroke-width="4" d="M5 5h633M5 5h633m0 0v364m0-364v364m0 0H5m633 0H5m0 0V5m0 364V5m242 0v364m0-364v364M5 144h242M5 144h242M154 5v139m0-139v139m0-53h93m-93 0h93m-57 0v53m0-53v53m-36-33h36m-36 0h36m-14-20v20m0-20v20m0-8h14m-14 0h14"/><path stroke-linecap="round" stroke-width="12" d="m638 5-5 57m5-57-5 57m0 0-14 55m14-55-14 55m0 0-24 53m24-53-24 53m0 0-32 49m32-49-32 49m0 0-40 43m40-43-40 43m0 0-46 37m46-37-46 37m0 0-53 30m53-30-53 30m0 0-56 22m56-22-56 22m0 0-60 13m60-13-60 13m0 0-61 5m61-5-61 5m0 0s0 0 0 0m0 0s0 0 0 0m0 0-38-3m38 3-38-3m0 0-37-8m37 8-37-8m0 0-35-14m35 14-35-14m0 0-32-18m32 18-32-18m0 0-29-23m29 23-29-23m0 0-25-27m25 27-25-27m0 0-20-30m20 30-20-30m0 0-14-33m14 33-14-33m0 0-9-34m9 34-9-34m0 0-3-35m3 35-3-35m0 0s0 0 0 0m0 0s0 0 0 0m0 0 2-22m-2 22 2-22m0 0 5-21m-5 21 5-21m0 0 9-20m-9 20 9-20m0 0 13-19M21 81l13-19m0 0 15-16M34 62l15-16m0 0 18-14M49 46l18-14m0 0 20-12M67 32c4-3 8-6 20-12m0 0 21-8m-21 8 21-8m0 0 23-5m-23 5 23-5m0 0 23-2m-23 2 23-2m0 0s0 0 0 0m0 0s0 0 0 0m0 0 15 1m-15-1 15 1m0 0 14 3m-14-3 14 3m0 0 13 5m-13-5 13 5m0 0 13 7m-13-7 13 7m0 0 11 9m-11-9 11 9m0 0 9 10m-9-10 9 10m0 0 8 12m-8-12 8 12m0 0 5 12m-5-12 5 12m0 0 4 13m-4-13 4 13m0 0 1 14m-1-14 1 14m0 0s0 0 0 0m0 0s0 0 0 0m0 0-1 8m1-8-1 8m0 0-2 8m2-8-2 8m0 0-4 8m4-8-4 8m0 0-4 7m4-7-4 7m0 0-6 6m6-6-6 6m0 0-7 6m7-6-7 6m0 0-7 4m7-4-7 4m0 0-9 3m9-3-9 3m0 0-8 2m8-2-8 2m0 0-9 1m9-1-9 1m0 0s0 0 0 0m0 0s0 0 0 0m0 0h-6m6 0h-6m0 0-5-2m5 2-5-2m0 0-5-2m5 2-5-2m0 0-5-2m5 2-5-2m0 0-4-4m4 4-4-4m0 0-4-4m4 4-4-4m0 0-3-4m3 4-3-4m0 0-2-5m2 5-2-5m0 0-1-5m1 5-1-5m0 0-1-5m1 5-1-5m0 0s0 0 0 0m0 0s0 0 0 0m0 0 1-3m-1 3 1-3m0 0v-3m0 3v-3m0 0 2-3m-2 3 2-3m0 0 2-3m-2 3 2-3m0 0 2-2m-2 2 2-2m0 0 2-2m-2 2 2-2m0 0 3-2m-3 2 3-2m0 0 3-1m-3 1 3-1m0 0 4-1m-4 1 4-1m0 0h3m-3 0h3m0 0s0 0 0 0m0 0s0 0 0 0m0 0h2m-2 0h2m0 0h2m-2 0h2m0 0 2 1m-2-1 2 1m0 0 2 1m-2-1 2 1m0 0 2 2m-2-2 2 2m0 0 1 1m-1-1 1 1m0 0 1 2m-1-2 1 2m0 0 1 2m-1-2 1 2m0 0v1m0-1v1m0 0 1 2m-1-2 1 2"/></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -29,7 +29,7 @@ try {
elHeight /= 1.1;
}
} else if (elWidth * elHeight < areaAvailable) {
while (elWidth * elHeight > areaAvailable) {
while (elWidth * elHeight < areaAvailable) {
elWidth *= 1.1;
elHeight *= 1.1;
}
@@ -64,4 +64,4 @@ try {
ea.addElementsToView(false, true, true);
} catch (err) {
_ = new Notice(err.toString())
}
}

View File

@@ -1,75 +0,0 @@
/*
Adds support for pen inversion, a.k.a. the hardware eraser on the back of your pen.
Simply use the eraser on a supported pen, and it will erase. Your previous tool will be restored when the eraser leaves the screen.
(Tested with a surface pen, but should work with all windows ink devices, and probably others)
**Note:** This script will stay active until the *Obsidian* window is closed.
Compatible with my *Auto Draw for Pen* script
```javascript
*/
(function() {
'use strict';
let activated
let revert
function handlePointer(e) {
const activeTool = ea.getExcalidrawAPI().getAppState().activeTool;
const isEraser = e.pointerType === 'pen' && e.buttons & 32
function setActiveTool(t) {
ea.getExcalidrawAPI().setActiveTool(t)
}
if (!activated && isEraser) {
//Store previous tool
const btns = document.querySelectorAll('.App-toolbar input.ToolIcon_type_radio')
for (const i in btns) {
if (btns[i]?.checked) {
revert = btns[i]
}
}
revert = activeTool
// Activate eraser tool
setActiveTool({type: "eraser"})
activated = true
// Force Excalidraw to recognize this the same as pen tip
// https://github.com/excalidraw/excalidraw/blob/4a9fac2d1e5c4fac334201ef53c6f5d2b5f6f9f5/src/components/App.tsx#L2945-L2951
Object.defineProperty(e, 'button', {
value: 0,
writable: false
});
}
// Keep on eraser!
if (isEraser && activated) {
setActiveTool({type: "eraser"})
}
if (activated && !isEraser) {
// Revert tool on release
// revert.click()
setActiveTool(revert)
activated = false
// Force delete "limbo" elements
// This doesn't happen on the web app
// It's a bug caused by switching to eraser during a stroke
ea.setView("active");
var del = []
for (const i in ea.getViewElements()) {
const element = ea.getViewElements()[i];
if (element.opacity === 20) {
del.push(element)
}
}
ea.deleteViewElements(del)
setActiveTool(revert)
}
}
window.addEventListener('pointerdown', handlePointer, { capture: true })
window.addEventListener('pointermove', handlePointer, { capture: true })
})();

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 448 512" style="enable-background:new 0 0 448 512;" xml:space="preserve">
<style type="text/css">
.st0{stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
</style>
<path class="st0" d="M355.8,234.1"/>
<g>
<path class="st0" d="M404.8,293.5L306.9,208l-120,137.4l97.9,85.5c13.6,11.9,34.4,10.5,46.3-3.1l76.8-88
C419.9,326.2,418.5,305.5,404.8,293.5z M389.4,322.2l-78.2,89.6c-3.8,4.3-10.4,4.8-14.8,1l-77.8-68l92-105.3l77.8,68
C392.8,311.2,393.2,317.8,389.4,322.2z"/>
<polygon class="st0" points="52.4,103.7 64.4,238.9 93,263.8 213,126.4 184.4,101.4 "/>
<rect x="108.3" y="185.1" transform="matrix(0.6578 -0.7532 0.7532 0.6578 -108.9276 230.7956)" class="st0" width="182.4" height="100.3"/>
<path class="st0" d="M109.7,381.6c-23.7-22.2-40-49.3-48.9-78.2c8.2-0.9,22.4-3.6,30.1-12.3c-12.6-12.5-25.3-25-37.9-37.5
c0-0.1,0-0.3,0-0.4l-23.6-22c-6,60.7,15.5,123.7,63.7,168.8s112.5,62.4,172.7,52.4l-24.1-22.6C194.8,432.3,146.9,416.4,109.7,381.6
z"/>
<path class="st0" d="M368.8,105.4c-56-52.4-133.5-67.2-201.3-45.5l21,19.6c56.1-13.2,117.7,1.1,163.1,43.7
c33,30.9,51.7,71.2,55.9,112.7c-0.2-0.4-0.3-0.6-0.3-0.6s-25.1-0.1-36.5,12.7c11.8,11.6,23.5,23.3,35.3,34.9c0,0,0,0.1,0,0.1
l2.4,2.3c0.4,0.4,0.9,0.9,1.3,1.3c0,0,0,0,0,0l17.7,16.6C444.7,234.1,424.8,157.7,368.8,105.4z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -33,8 +33,20 @@ const invertColor = (color) => {
}
}
const invertPaletteColors = (palette) => Object.keys(palette).forEach(key => palette[key] = invertColor(palette[key]));
Object.keys(colorPalette).forEach(key => invertPaletteColors(colorPalette[key]));
function invertColorsRecursively(obj) {
if (typeof obj === 'string') {
return invertColor(obj);
} else if (Array.isArray(obj)) {
return obj.map(item => invertColorsRecursively(item));
} else if (typeof obj === 'object' && obj !== null) {
const result = {};
Object.keys(obj).forEach(key => result[key] = invertColorsRecursively(obj[key]));
return result;
} else {
return obj;
}
}
colorPalette = invertColorsRecursively(colorPalette);
ea.copyViewElementsToEAforEditing(ea.getViewElements());
ea.getElements().forEach(el=>{

View File

@@ -0,0 +1,36 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-organic-line-legacy.jpg)
Converts selected freedraw lines such that pencil pressure will decrease from maximum to minimum from the beginning of the line to its end. The resulting line is placed at the back of the layers, under all other items. Helpful when drawing organic mindmaps.
This is the old script from this [video](https://youtu.be/JMcNDdj_lPs?t=479). Since it's release this has been superseded by custom pens that you can enable in plugin settings. For more on custom pens, watch [this](https://youtu.be/OjNhjaH2KjI)
The benefit of the approach in this implementation of custom pens is that it will look the same on excalidraw.com when you copy your drawing over for sharing with non-Obsidian users. Otherwise custom pens are faster to use and much more configurable.
```javascript
*/
let elements = ea.getViewSelectedElements().filter((el)=>["freedraw","line","arrow"].includes(el.type));
if(elements.length === 0) {
elements = ea.getViewSelectedElements();
const len = elements.length;
if(len === 0 || ["freedraw","line","arrow"].includes(elements[len].type)) {
return;
}
elements = [elements[len]];
}
ea.copyViewElementsToEAforEditing(elements);
ea.getElements().forEach((el)=>{
el.simulatePressure = false;
el.type = "freedraw";
el.pressures = [];
const len = el.points.length;
for(i=0;i<len;i++)
el.pressures.push((len-i)/len);
});
await ea.addElementsToView(false,true);
elements.forEach((el)=>ea.moveViewElementToZIndex(el.id,0));
const ids=ea.getElements().map(el=>el.id);
ea.selectElementsInView(ea.getViewElements().filter(el=>ids.contains(el.id)));

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@@ -33,6 +33,7 @@ Open the script you are interested in and save it to your Obsidian Vault includi
|[Add Link to Existing File and Open](Add%20Link%20to%20Existing%20File%20and%20Open.md)|Prompts for a file from the vault. Adds a link to the selected element pointing to the selected file. You can control in settings to open the file in the current active pane or an adjacent pane.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-link-and-open.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Add Link to New Page and Open](Add%20Link%20and%20Open%20Page.md)|Prompts for filename. Offers option to create and open a new Markdown or Excalidraw document. Adds link pointing to the new file, to the selected objects in the drawing. You can control in settings to open the file in the current active pane or an adjacent pane.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-link-to-new-page-and-pen.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Add Next Step in Process](Add%20Link%20to%20New%20Page%20and%20Open.md)|This script will prompt you for the title of the process step, then will create a stick note with the text. If an element is selected then the script will connect this new step with an arrow to the previous step (the selected element). If no element is selected, then the script assumes this is the first step in the process and will only output the sticky note with the text that was entered.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-process-step.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Split Ellipse](Boolean%20Operations.md)|With This Script it is possible to make boolean Operations on Shapes.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-boolean-operations-showcase.png)|[@GColoy](https://github.com/GColoy)|
|[Box Each Selected Groups](Box%20Each%20Selected%20Groups.md)|This script will add encapsulating boxes around each of the currently selected groups in Excalidraw.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-each-selected-groups.png)|[@1-2-3](https://github.com/1-2-3)|
|[Box Selected Elements](Box%20Selected%20Elements.md)|This script will add an encapsulating box around the currently selected elements in Excalidraw.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-box-elements.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Change shape of selected elements](Change%20shape%20of%20selected%20elements.md)|The script allows you to change the shape of selected Rectangles, Diamonds and Ellipses|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-change-shape.jpg)|[@zsviczian](https://github.com/zsviczian)|
@@ -57,9 +58,10 @@ Open the script you are interested in and save it to your Obsidian Vault includi
|[Mindmap connector](Mindmap%20connector.md)|This script creates mindmap like lines (only right side and down available currently) for selected elements. The line will start according to the creation time of the elements. So you should create the header element first.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/mindmap%20connector.png)|[@xllowl](https://github.com/xllowl)|
|[Modify background color opacity](Modify%20background%20color%20opacity.md)|This script changes the opacity of the background color of the selected boxes. The default background color in Excalidraw is so dark that the text is hard to read. You can lighten the color a bit by setting transparency. And you can tweak the transparency over and over again until you're happy with it. Although excalidraw has the opacity option in its native property Settings, it also changes the transparency of the border. Use this script to change only the opacity of the background color without affecting the border.|![](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)|
|[Normalize Selected Arrows](Normalize%20Selected%20Arrows.md)|This script will reset the start and end positions of the selected arrows. The arrow will point to the center of the connected box and will have a gap of 8px from the box.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-normalize-selected-arrows.png)|[@1-2-3](https://github.com/1-2-3)|
|[OCR - Optical Character Recognition](OCR%20-%20Optical%20Character%20Recognition.md)|The script will 1) send the selected image file to [taskbone.com](https://taskbone.com) to exctract the text from the image, and 2) will add the text to your drawing as a text element.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-ocr.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[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 extract 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)|
|[Organic Line](Organic%20Line.md)|Converts selected freedraw lines such that pencil pressure will decrease from maximum to minimum from the beginning of the line to its end. The resulting line is placed at the back of the layers, under all other items. Helpful when drawing organic mindmaps.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-organic-line.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Repeat Elements](Repeat%20Elements.md)|This script will detect the difference between 2 selected elements, including position, size, angle, stroke and background color, and create several elements that repeat these differences based on the number of repetitions entered by the user.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-repeat-elements.png)|[@1-2-3](https://github.com/1-2-3)|
|[Reset LaTeX Size](Reset%20LaTeX%20Size.md)|Reset the sizes of embedded LaTeX equations to the default sizes or a multiple of the default sizes.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-reset-latex.jpg)|[@firai](https://github.com/firai)|
|[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)|
|[Scribble Helper](Scribble%20Helper.md)|iOS scribble helper for better handwriting experience with text elements. If no elements are selected then the creates a text element at pointer position and you can use the edit box to modify the text with scribble. If a text element is selected then opens the input prompt where you can modify this text with scribble.|![]('https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-scribble-helper.jpg')|[@zsviczian](https://github.com/zsviczian)|
|[Select Elements of Type](Select%20Elements%20of%20Type.md)|Prompts you with a list of the different element types in the active image. Only elements of the selected type will be selected on the canvas. If nothing is selected when running the script, then the script will process all the elements on the canvas. If some elements are selected when the script is executed, then the script will only process the selected elements.<br>The script is useful when, for example, you want to bring to front all the arrows, or want to change the color of all the text elements, etc.|![]('https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-select-element-of-type.jpg')|[@zsviczian](https://github.com/zsviczian)|
@@ -71,8 +73,10 @@ Open the script you are interested in and save it to your Obsidian Vault includi
|[Set stroke width of selected elements](Set%20Stroke%20Width%20of%20Selected%20Elements.md)|This script will set the stroke width of selected elements. This is helpful, for example, when you scale freedraw sketches and want to reduce or increase their line width.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-stroke-width.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)|
|[Set Text Alignment](Set%20Text%20Alignment.md)|Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-align.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Split Ellipse](Split%20Ellipse.md)|This script splits an ellipse at any point where a line intersects it.|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-splitEllipse-demo1.png)|[@GColoy](https://github.com/GColoy)|
|[TheBrain-navigation](TheBrain-navigation.md)|An Excalidraw based graph user interface for your Vault. Requires the [Dataview plugin](https://github.com/blacksmithgu/obsidian-dataview). Generates a graph view similar to that of [TheBrain](https://TheBrain.com) plex. Watch introduction to this script on [YouTube](https://youtu.be/plYobK-VufM).|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/TheBrain.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Toggle Fullscreen on Mobile](Toggle%20Fullscreen%20on%20Mobile.md)|Hides Obsidian workspace leaf padding and header (based on option in settings, default is "hide header" = false) which will take Excalidraw to full screen. ⚠ Note that if the header is not visible, it will be very difficult to invoke the command palette to end full screen. Only hide the header if you have a keyboard or you've practiced opening command palette!|![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/ea-toggle-fullscreen.jpg)|[@zsviczian](https://github.com/zsviczian)|
|[Toggle Grid](Toggle%20Grid.md)|Toggles the grid.||[@GColoy](https://github.com/GColoy)|
|[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 <kbd>SHIFT+2</kbd> feature: Zoom to fit selected elements, but with the ability to zoom to 1000%. Inspiration: [#272](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/272)||[@zsviczian](https://github.com/zsviczian)|
|[Hardware Eraser Suppoer](Hardware%20Eraser%20Support.md)|Allows the use of pen inversion/hardware erasers on supported pens.|[@threethan](https://github.com/threethan)|

View File

@@ -0,0 +1,40 @@
/*
The script will cycle through S, M, L, XL font sizes scaled to the current canvas zoom.
```js*/
const FONTSIZES = [16, 20, 28, 36];
const api = ea.getExcalidrawAPI();
const st = api.getAppState();
const zoom = st.zoom.value;
const currentItemFontSize = st.currentItemFontSize;
const fontsizes = FONTSIZES.map(s=>s/zoom);
const els = ea.getViewSelectedElements().filter(el=>el.type === "text");
const findClosestIndex = (val, list) => {
let closestIndex = 0;
let closestDifference = Math.abs(list[0] - val);
for (let i = 1; i < list.length; i++) {
const difference = Math.abs(list[i] - val);
if (difference <= closestDifference) {
closestDifference = difference;
closestIndex = i;
}
}
return closestIndex;
}
ea.viewUpdateScene({appState:{currentItemFontSize: fontsizes[(findClosestIndex(currentItemFontSize, fontsizes)+1) % fontsizes.length] }});
if(els.length>0) {
ea.copyViewElementsToEAforEditing(els);
ea.getElements().forEach(el=> {
el.fontSize = fontsizes[(findClosestIndex(el.fontSize, fontsizes)+1) % fontsizes.length];
const font = ExcalidrawLib.getFontString(el);
const lineHeight = ExcalidrawLib.getDefaultLineHeight(el.fontFamily);
const {width, height, baseline} = ExcalidrawLib.measureText(el.originalText, font, lineHeight);
el.width = width;
el.height = height;
el.baseline = baseline;
});
ea.addElementsToView();
}

View File

@@ -0,0 +1,4 @@
<svg viewBox="0 0 50 30" xmlns="http://www.w3.org/2000/svg">
<text fill="currentColor" x="10" y="30" font-size="16px" font-weight="light">A</text>
<text fill="currentColor" x="22" y="30" font-size="36px" font-weight="light">A</text>
</svg>

After

Width:  |  Height:  |  Size: 243 B

View File

@@ -0,0 +1,96 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-repeat-texts.png)
In the following script, we address the concept of repetition through the lens of numerical progression. As visualized by the image, where multiple circles each labeled with an even task number are being condensed into a linear sequence, our script will similarly iterate through a set of numbers.
Inspired from [Repeat Elements](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Repeat%20Elements.md)
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.7.19")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
let repeatNum = parseInt(await utils.inputPrompt("repeat times?","number","5"));
if(!repeatNum) {
new Notice("Please enter a number.");
return;
}
const selectedElements = ea.getViewSelectedElements().sort((lha,rha) => lha.x === rha.x ? lha.y - rha.y : lha.x - rha.x);
const selectedBounds = selectedElements.filter(e => e.type !== "text");
const selectedTexts = selectedElements.filter(e => e.type === "text");
const selectedTextsById = selectedTexts.reduce((prev, next) => (prev[next.id] = next, prev), {})
if(selectedTexts.length !== 2 || ![0, 2].includes(selectedBounds.length)) {
new Notice("Please select only 2 text elements.");
return;
}
if(selectedBounds.length === 2) {
if(selectedBounds[0].type !== selectedBounds[1].type) {
new Notice("The selected elements must be of the same type.");
return;
}
if (!selectedBounds.every(e => e.boundElements?.length === 1)) {
new Notice("Only support the bound element with 1 text element.");
return;
}
if (!selectedBounds.every(e => !!selectedTextsById[e.boundElements?.[0]?.id])) {
new Notice("Bound element must refer to the text element.");
return;
}
}
const prevBoundEl = selectedBounds.length ? selectedBounds[0] : selectedTexts[0];
const nextBoundEl = selectedBounds.length ? selectedBounds[1] : selectedTexts[1];
const prevTextEl = prevBoundEl.type === 'text' ? prevBoundEl : selectedTextsById[prevBoundEl.boundElements[0].id]
const nextTextEl = nextBoundEl.type === 'text' ? nextBoundEl : selectedTextsById[nextBoundEl.boundElements[0].id]
const xDistance = nextBoundEl.x - prevBoundEl.x;
const yDistance = nextBoundEl.y - prevBoundEl.y;
const numReg = /\d+/
let textNumDiff
try {
const num0 = +prevTextEl.text.match(numReg)
const num1 = +nextTextEl.text.match(numReg)
textNumDiff = num1 - num0
} catch(e) {
new Notice("Text must include a number!")
return;
}
const repeatEl = (newEl, step) => {
ea.elementsDict[newEl.id] = newEl;
newEl.x += xDistance * (step + 1);
newEl.y += yDistance * (step + 1);
if(newEl.text) {
const text = newEl.text.replace(numReg, (match) => +match + (step + 1) * textNumDiff)
newEl.originalText = text
newEl.rawText = text
newEl.text = text
}
}
ea.copyViewElementsToEAforEditing(selectedBounds);
for(let i=0; i<repeatNum; i++) {
const newTextEl = ea.cloneElement(nextTextEl);
repeatEl(newTextEl, i)
if (selectedBounds.length) {
const newBoundEl = ea.cloneElement(selectedBounds[1]);
newBoundEl.boundElements[0].id = newTextEl.id
newTextEl.containerId = newBoundEl.id
repeatEl(newBoundEl, i)
}
}
await ea.addElementsToView(false, false, true);

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve">
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
<g><g><g><path fill="#000000" d="M101.2,18.7c-2,0.9-38,36.6-39.2,39c-1,1.9-1.1,4.2-0.4,6.2c0.6,1.5,37.3,38.6,39.3,39.6c1.7,0.9,5.2,0.8,7.1-0.1c1.9-0.9,3.7-3.5,4.1-5.8c0.6-3.5-0.2-4.5-12.5-16.8L88.2,69.2h44.3c38.7,0,44.8,0.1,48.6,0.7c24.2,4.2,43.2,22.6,48.1,46.8c3.3,16.4-1,34.2-11.5,47.6c-3.5,4.6-4.1,6.9-2.4,10.4c1.8,3.7,6.6,5.3,10.4,3.5c2.8-1.3,8.7-9.4,12.5-17.2c10.5-20.8,10.5-45.2,0-66.2c-10.3-20.6-28.6-34.8-51.8-40c-4.6-1.1-4.8-1.1-51.3-1.2c-25.7-0.1-46.7-0.3-46.7-0.5s5.2-5.5,11.5-11.9c9.4-9.4,11.6-11.9,12-13.3C113.6,21.6,107.3,16.1,101.2,18.7z"/><path fill="#000000" d="M34.9,73.4c-1.8,0.5-5.8,4.4-9.7,9.6c-7.9,10.4-12.8,22.4-14.5,35.4c-5.3,40.1,22.9,77.3,63.1,83.3c4,0.6,11.3,0.7,45.4,0.7c24,0,40.6,0.2,40.6,0.4c0,0.2-5,5.3-11.1,11.1c-11.3,11-12.9,13-12.9,16c0,4.1,3.8,7.9,7.9,7.9c0.7,0,2.1-0.3,3.1-0.7c2.3-1,38.1-36.6,39.3-39.2c1-2.1,1.1-4.2,0.4-6.1c-0.6-1.5-37.3-38.6-39.3-39.7c-1.9-1-6.1-0.6-8.1,0.8c-2.9,2-4,6.4-2.5,9.4c0.4,0.8,5.9,6.6,12.1,12.9l11.4,11.4h-40.4c-35,0-40.9-0.1-44.7-0.7c-24.2-4.1-43.4-22.9-48.2-47.1c-1.1-5.5-1.1-16.4,0-21.9c2.1-10.7,7-20.2,14.6-28.7c2.1-2.3,3.5-4.3,3.8-5.4c1-3.6-0.7-7.3-4.2-9C38.7,72.8,37.4,72.7,34.9,73.4z"/></g></g></g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,32 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-reset-latex.jpg)
Reset the sizes of embedded LaTeX equations to the default sizes or a multiple of the default sizes.
```javascript
*/
if (!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("2.4.0")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
let elements = ea.getViewSelectedElements().filter((el)=>["image"].includes(el.type));
if (elements.length === 0) return;
scale = await utils.inputPrompt("Scale?", "Number", "1");
if (!scale) return;
scale = parseFloat(scale);
ea.copyViewElementsToEAforEditing(elements);
for (el of elements) {
equation = ea.targetView.excalidrawData.getEquation(el.fileId)?.latex;
if (!equation) return;
eqData = await ea.tex2dataURL(equation);
ea.getElement(el.id).width = eqData.size.width * scale;
ea.getElement(el.id).height = eqData.size.height * scale;
};
ea.addElementsToView(false, false);

View File

@@ -0,0 +1 @@
<svg class="skip" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><rect stroke-width="2" width="20" height="16" x="2" y="4" rx="2"/><path stroke-width="2" d="M12 9v11"/><path stroke-width="2" d="M2 9h13a2 2 0 0 1 2 2v9"/></svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@@ -3,6 +3,8 @@
Scribble Helper can improve handwriting and add links. It lets you create and edit text elements, including wrapped text and sticky notes, by double-tapping on the canvas. When you run the script, it creates an event handler that will activate the editor when you double-tap. If you select a text element on the canvas before running the script, it will open the editor for that element. If you use a pen, you can set it up to only activate Scribble Helper when you double-tap with the pen. The event handler is removed when you run the script a second time or switch to a different tab.
<iframe width="560" height="315" src="https://www.youtube.com/embed/BvYkOaly-QM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.8.25")) {

View File

@@ -0,0 +1,206 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-select-similar-elements.png)
This script enables the selection of elements based on matching properties. Select the attributes (such as stroke color, fill style, font family, etc) that should match for selection. It's perfect for large scenes where manual selection of elements would be cumbersome. You can either run the script to select matching elements across the entire scene, or define a specific group of elements to apply the selection criteria to.
```js */
let config = window.ExcalidrawSelectConfig;
config = Boolean(config) && (Date.now() - config.timestamp < 60000) ? config : null;
let elements = ea.getViewSelectedElements();
if(!config && (elements.length !==1)) {
new Notice("Select a single element");
return;
} else {
if(elements.length === 0) {
elements = ea.getViewElements();
}
}
const {angle, backgroundColor, fillStyle, fontFamily, fontSize, height, width, opacity, roughness, roundness, strokeColor, strokeStyle, strokeWidth, type, startArrowhead, endArrowhead, fileId} = ea.getViewSelectedElement();
const fragWithHTML = (html) => createFragment((frag) => (frag.createDiv().innerHTML = html));
//--------------------------
// RUN
//--------------------------
const run = () => {
selectedElements = elements.filter(el=>
((typeof config.angle === "undefined") || (el.angle === config.angle)) &&
((typeof config.backgroundColor === "undefined") || (el.backgroundColor === config.backgroundColor)) &&
((typeof config.fillStyle === "undefined") || (el.fillStyle === config.fillStyle)) &&
((typeof config.fontFamily === "undefined") || (el.fontFamily === config.fontFamily)) &&
((typeof config.fontSize === "undefined") || (el.fontSize === config.fontSize)) &&
((typeof config.height === "undefined") || Math.abs(el.height - config.height) < 0.01) &&
((typeof config.width === "undefined") || Math.abs(el.width - config.width) < 0.01) &&
((typeof config.opacity === "undefined") || (el.opacity === config.opacity)) &&
((typeof config.roughness === "undefined") || (el.roughness === config.roughness)) &&
((typeof config.roundness === "undefined") || (el.roundness === config.roundness)) &&
((typeof config.strokeColor === "undefined") || (el.strokeColor === config.strokeColor)) &&
((typeof config.strokeStyle === "undefined") || (el.strokeStyle === config.strokeStyle)) &&
((typeof config.strokeWidth === "undefined") || (el.strokeWidth === config.strokeWidth)) &&
((typeof config.type === "undefined") || (el.type === config.type)) &&
((typeof config.startArrowhead === "undefined") || (el.startArrowhead === config.startArrowhead)) &&
((typeof config.endArrowhead === "undefined") || (el.endArrowhead === config.endArrowhead)) &&
((typeof config.fileId === "undefined") || (el.fileId === config.fileId))
)
ea.selectElementsInView(selectedElements);
delete window.ExcalidrawSelectConfig;
}
//--------------------------
// Modal
//--------------------------
const showInstructions = () => {
const instructionsModal = new ea.obsidian.Modal(app);
instructionsModal.onOpen = () => {
instructionsModal.contentEl.createEl("h2", {text: "Instructions"});
instructionsModal.contentEl.createEl("p", {text: "Step 1: Choose the attributes that you want the selected elements to match."});
instructionsModal.contentEl.createEl("p", {text: "Step 2: Select an action:"});
instructionsModal.contentEl.createEl("ul", {}, el => {
el.createEl("li", {text: "Click 'RUN' to find matching elements throughout the entire scene."});
el.createEl("li", {text: "Click 'SELECT' to 1) first choose a specific group of elements in the scene, then 2) run the 'Select Similar Elements' once more within 1 minute to apply the filter criteria only to that group of elements."});
});
instructionsModal.contentEl.createEl("p", {text: "Note: If you choose 'SELECT', make sure to click the 'Select Similar Elements' script again within 1 minute to apply your selection criteria to the group of elements you chose."});
};
instructionsModal.open();
};
const selectAttributesToCopy = () => {
const configModal = new ea.obsidian.Modal(app);
configModal.onOpen = () => {
config = {};
configModal.contentEl.createEl("h1", {text: "Select Similar Elements"});
new ea.obsidian.Setting(configModal.contentEl)
.setDesc("Choose the attributes you want the selected elements to match, then select an action.")
.addButton(button => button
.setButtonText("Instructions")
.onClick(showInstructions)
);
// Add Toggles for the rest of the attributes
let attributes = [
{name: "Element type", key: "type"},
{name: "Stroke color", key: "strokeColor"},
{name: "Background color", key: "backgroundColor"},
{name: "Opacity", key: "opacity"},
{name: "Fill style", key: "fillStyle"},
{name: "Stroke style", key: "strokeStyle"},
{name: "Stroke width", key: "strokeWidth"},
{name: "Roughness", key: "roughness"},
{name: "Roundness", key: "roundness"},
{name: "Font family", key: "fontFamily"},
{name: "Font size", key: "fontSize"},
{name: "Start arrowhead", key: "startArrowhead"},
{name: "End arrowhead", key: "endArrowhead"},
{name: "Height", key: "height"},
{name: "Width", key: "width"},
{name: "ImageID", key: "fileId"},
];
attributes.forEach(attr => {
const attrValue = elements[0][attr.key];
if((typeof attrValue !== "undefined" && attrValue !== null) || (attr.key === "startArrowhead" && elements[0].type === "arrow") || (attr.key === "endArrowhead" && elements[0].type === "arrow")) {
let description = '';
switch(attr.key) {
case 'backgroundColor':
case 'strokeColor':
description = `<div style='background-color:${attrValue};'>${attrValue}</div>`;
break;
case 'roundness':
description = attrValue === null ? 'Sharp' : 'Round';
break;
case 'roughness':
description = attrValue === 0 ? 'Architect' : attrValue === 1 ? 'Artist' : 'Cartoonist';
break;
case 'strokeWidth':
description = attrValue <= 0.5 ? 'Extra thin' :
attrValue <= 1 ? 'Thin' :
attrValue <= 2 ? 'Bold' :
'Extra bold';
break;
case 'opacity':
description = `${attrValue}%`;
break;
case 'width':
case 'height':
description = `${attrValue.toFixed(2)}`;
break;
case 'startArrowhead':
case 'endArrowhead':
description = attrValue === null ? 'None' : `${attrValue.charAt(0).toUpperCase() + attrValue.slice(1)}`;
break;
case 'fontFamily':
description = attrValue === 1 ? 'Hand-drawn' :
attrValue === 2 ? 'Normal' :
attrValue === 3 ? 'Code' :
'Custom 4th font';
break;
case 'fontSize':
description = `${attrValue}`;
break;
default:
description = `${attrValue.charAt(0).toUpperCase() + attrValue.slice(1)}`;
break;
}
new ea.obsidian.Setting(configModal.contentEl)
.setName(`${attr.name}`)
.setDesc(fragWithHTML(`${description}`))
.addToggle(toggle => toggle
.setValue(false)
.onChange(value => {
if(value) {
config[attr.key] = attrValue;
} else {
delete config[attr.key];
}
})
)
}
});
//Add Toggle for the rest of the attributes. Organize attributes into a logical sequence or groups by adding
//configModal.contentEl.createEl("h") or similar to the code
new ea.obsidian.Setting(configModal.contentEl)
.addButton(button => button
.setButtonText("SELECT")
.onClick(()=>{
config.timestamp = Date.now();
window.ExcalidrawSelectConfig = config;
configModal.close();
})
)
.addButton(button => button
.setButtonText("RUN")
.setCta(true)
.onClick(()=>{
elements = ea.getViewElements();
run();
configModal.close();
})
)
}
configModal.onClose = () => {
setTimeout(()=>{
delete configModal
});
}
configModal.open();
}
if(config) {
run();
} else {
selectAttributesToCopy();
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-filter"><polygon fill="none" stroke-width="2" points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>

After

Width:  |  Height:  |  Size: 285 B

View File

@@ -8,18 +8,84 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.8.11")) {
if(ea.verifyMinimumPluginVersion && ea.verifyMinimumPluginVersion("2.4.0")) {
const api = ea.getExcalidrawAPI();
let appState = api.getAppState();
let gridFrequency = appState.gridStep;;
const customControls = (container) => {
new ea.obsidian.Setting(container)
.setName(`Major grid frequency`)
.addDropdown(dropdown => {
[2,3,4,5,6,7,8,9,10].forEach(grid=>dropdown.addOption(grid,grid));
dropdown
.setValue(gridFrequency)
.onChange(value => {
gridFrequency = value;
})
})
}
const gridSize = parseInt(await utils.inputPrompt(
"Grid size?",
null,
appState.GridSize?.toString()??"20",
null,
1,
false,
customControls
));
if(isNaN(gridSize)) return; //this is to avoid passing an illegal value to Excalidraw
const gridStep = isNaN(parseInt(gridFrequency)) ? appState.gridStep : parseInt(gridFrequency);
api.updateScene({
appState : {gridSize, gridStep, gridModeEnabled:true},
commitToHistory:false
});
}
// ----------------
// old script
// ----------------
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.9.19")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
const api = ea.getExcalidrawAPI();
let appState = api.getAppState();
const grid = parseInt(await utils.inputPrompt("Grid size?",null,appState.previousGridSize?.toString()??"20"));
const gridColor = appState.gridColor;
let gridFrequency = gridColor?.MajorGridFrequency ?? 5;
const customControls = (container) => {
new ea.obsidian.Setting(container)
.setName(`Major grid frequency`)
.addDropdown(dropdown => {
[2,3,4,5,6,7,8,9,10].forEach(grid=>dropdown.addOption(grid,grid));
dropdown
.setValue(gridFrequency)
.onChange(value => {
gridFrequency = value;
})
})
}
const grid = parseInt(await utils.inputPrompt(
"Grid size?",
null,
appState.previousGridSize?.toString()??"20",
null,
1,
false,
customControls
));
if(isNaN(grid)) return; //this is to avoid passing an illegal value to Excalidraw
appState.gridSize = grid;
appState.previousGridSize = grid;
if(gridColor) gridColor.MajorGridFrequency = parseInt(gridFrequency);
api.updateScene({
appState,
appState : {gridSize: grid, previousGridSize: grid, gridColor},
commitToHistory:false
});

View File

@@ -9,7 +9,11 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
```javascript
*/
let width = (ea.getViewSelectedElement().strokeWidth??1).toString();
width = await utils.inputPrompt("Width?","number",width);
width = parseFloat(await utils.inputPrompt("Width?","number",width));
if(isNaN(width)) {
new Notice("Invalid number");
return;
}
const elements=ea.getViewSelectedElements();
ea.copyViewElementsToEAforEditing(elements);
ea.getElements().forEach((el)=>el.strokeWidth=width);

File diff suppressed because it is too large Load Diff

208
ea-scripts/Split Ellipse.md Normal file
View File

@@ -0,0 +1,208 @@
/*
This script splits an ellipse at any point where a line intersects it. If no lines are selected, it will use every line that intersects the ellipse. Otherwise, it will only use the selected lines. If there is no intersecting line, the ellipse will be converted into a line object.
There is also the option to close the object along the cut, which will close the cut in the shape of the line.
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-splitEllipse-demo1.jpg)
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-splitEllipse-demo2.jpg)
Tip: To use an ellipse as the cutting object, you first have to use this script on it, since it will convert the ellipse into a line.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
const elements = ea.getViewSelectedElements();
const ellipse = elements.filter(el => el.type == "ellipse")[0];
if (!ellipse) return;
let lines = elements.filter(el => el.type == "line" || el.type == "arrow");
if (lines.length == 0) lines = ea.getViewElements().filter(el => el.type == "line" || el.type == "arrow");
const subLines = getSubLines(lines);
const angles = subLines.flatMap(line => {
return intersectionAngleOfEllipseAndLine(ellipse, line.a, line.b).map(result => ({
angle: result,
cuttingLine: line
}));
});
if (angles.length === 0) angles.push({ angle: 0, cuttingLine: null });
angles.sort((a, b) => a.angle - b.angle);
const closeObject = await utils.suggester(["Yes", "No"], [true, false], "Close object along cutedge?")
ea.style.strokeSharpness = closeObject ? "sharp" : "round";
ea.style.strokeColor = ellipse.strokeColor;
ea.style.strokeWidth = ellipse.strokeWidth;
ea.style.backgroundColor = ellipse.backgroundColor;
ea.style.fillStyle = ellipse.fillStyle;
ea.style.roughness = ellipse.roughness;
angles.forEach((angle, key) => {
const cuttingLine = angle.cuttingLine;
angle = angle.angle;
const nextAngleKey = (key + 1) < angles.length ? key + 1 : 0;
const nextAngle = angles[nextAngleKey].angle;
const AngleDelta = nextAngle - angle ? nextAngle - angle : Math.PI*2;
const pointAmount = Math.ceil((AngleDelta*64)/(Math.PI*2));
const stepSize = AngleDelta/pointAmount;
let points = drawEllipse(ellipse.x, ellipse.y, ellipse.width, ellipse.height, ellipse.angle, angle, nextAngle, stepSize);
if (closeObject && cuttingLine) points = points.concat(getCutLine(points[0], angles[key], angles[nextAngleKey], ellipse));
const lineId = ea.addLine(points);
const line = ea.getElement(lineId);
line.frameId = ellipse.frameId;
line.groupIds = ellipse.groupIds;
});
ea.deleteViewElements([ellipse]);
ea.addElementsToView(false,false,true);
return;
function getSubLines(lines) {
return lines.flatMap((line, key) => {
return line.points.slice(1).map((pointB, i) => ({
a: addVectors([line.points[i], [line.x, line.y]]),
b: addVectors([pointB, [line.x, line.y]]),
originLineIndex: key,
indexPointA: i,
}));
});
}
function intersectionAngleOfEllipseAndLine(ellipse, pointA, pointB) {
/*
To understand the code in this function and subfunctions it might help to take a look at this geogebra file
https://www.geogebra.org/m/apbm3hs6
*/
const c = multiplyVectorByScalar([ellipse.width, ellipse.height], (1/2));
const a = rotateVector(
addVectors([
pointA,
invVec([ellipse.x, ellipse.y]),
invVec(multiplyVectorByScalar([ellipse.width, ellipse.height], (1/2)))
]),
-ellipse.angle
)
const l_b = rotateVector(
addVectors([
pointB,
invVec([ellipse.x, ellipse.y]),
invVec(multiplyVectorByScalar([ellipse.width, ellipse.height], (1/2)))
]),
-ellipse.angle
);
const b = addVectors([
l_b,
invVec(a)
]);
const solutions = calculateLineSegment(a[0], a[1], b[0], b[1], c[0], c[1]);
return solutions
.filter(num => isBetween(num, 0, 1))
.map(num => {
const point = [
(a[0] + b[0] * num) / ellipse.width,
(a[1] + b[1] * num) / ellipse.height
];
return angleBetweenVectors([1, 0], point);
});
}
function drawEllipse(x, y, width, height, angle = 0, start = 0, end = Math.PI*2, step = Math.PI/32) {
const ellipse = (t) => {
const spanningVector = rotateVector([width/2*Math.cos(t), height/2*Math.sin(t)], angle);
const baseVector = [x+width/2, y+height/2];
return addVectors([baseVector, spanningVector]);
}
if(end <= start) end = end + Math.PI*2;
let points = [];
const almostEnd = end - step/2;
for (let t = start; t < almostEnd; t = t + step) {
points.push(ellipse(t));
}
points.push(ellipse(end))
return points;
}
function getCutLine(startpoint, currentAngle, nextAngle, ellipse) {
if (currentAngle.cuttingLine.originLineIndex != nextAngle.cuttingLine.originLineIndex) return [];
const originLineIndex = currentAngle.cuttingLine.originLineIndex;
if (lines[originLineIndex] == 2) return startpoint;
const originLine = [];
lines[originLineIndex].points.forEach(p => originLine.push(addVectors([
p,
[lines[originLineIndex].x, lines[originLineIndex].y]
])));
const edgepoints = [];
const direction = isInEllipse(originLine[clamp(nextAngle.cuttingLine.indexPointA - 1, 0, originLine.length - 1)], ellipse) ? -1 : 1
let i = isInEllipse(originLine[nextAngle.cuttingLine.indexPointA], ellipse) ? nextAngle.cuttingLine.indexPointA : nextAngle.cuttingLine.indexPointA + direction;
while (isInEllipse(originLine[i], ellipse)) {
edgepoints.push(originLine[i]);
i = (i + direction) % originLine.length;
}
edgepoints.push(startpoint);
return edgepoints;
}
function calculateLineSegment(ax, ay, bx, by, cx, cy) {
const sqrt = Math.sqrt((cx ** 2) * (cy ** 2) * (-(ay ** 2) * (bx ** 2) + 2 * ax * ay * bx * by - (ax ** 2) * (by ** 2) + (bx ** 2) * (cy ** 2) + (by ** 2) * (cx ** 2)));
const numerator = -(ay * by * (cx ** 2) + ax * bx * (cy ** 2));
const denominator = ((by ** 2) * (cx ** 2) + (bx ** 2) * (cy ** 2));
const t1 = (numerator + sqrt) / denominator;
const t2 = (numerator - sqrt) / denominator;
return [t1, t2];
}
function isInEllipse(point, ellipse) {
point = addVectors([point, invVec([ellipse.x, ellipse.y]), invVec(multiplyVectorByScalar([ellipse.width, ellipse.height], 1/2))]);
point = [point[0]*2/ellipse.width, point[1]*2/ellipse.height];
const distance = Math.sqrt(point[0]**2 + point[1]**2);
return distance < 1;
}
function angleBetweenVectors(v1, v2) {
let dotProduct = v1[0] * v2[0] + v1[1] * v2[1];
let determinant = v1[0] * v2[1] - v1[1] * v2[0];
let angle = Math.atan2(determinant, dotProduct);
return angle < 0 ? angle + 2 * Math.PI : angle;
}
function rotateVector (vec, ang) {
var cos = Math.cos(ang);
var sin = Math.sin(ang);
return [vec[0] * cos - vec[1] * sin, vec[0] * sin + vec[1] * cos];
}
function addVectors(vectors) {
return vectors.reduce((acc, vec) => [acc[0] + vec[0], acc[1] + vec[1]], [0, 0]);
}
function invVec(vector) {
return [-vector[0], -vector[1]];
}
function multiplyVectorByScalar(vector, scalar) {
return [vector[0] * scalar, vector[1] * scalar];
}
function round(number, precision) {
var factor = Math.pow(10, precision);
return Math.round(number * factor) / factor;
}
function isBetween(num, min, max) {
return (num >= min && num <= max);
}
function clamp(number, min, max) {
return Math.max(min, Math.min(number, max));
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -18,10 +18,10 @@ elements.forEach((el)=>{
ea.style.strokeColor = el.strokeColor;
ea.style.fontFamily = el.fontFamily;
ea.style.fontSize = el.fontSize;
const text = el.text.split("\n");
const text = el.rawText.split("\n");
for(i=0;i<text.length;i++) {
ea.addText(el.x,el.y+i*el.height/text.length,text[i]);
ea.addText(el.x,el.y+i*el.height/text.length,text[i].trim());
}
});
ea.addElementsToView(false,false);
ea.addElementsToView(false,false,true);
ea.deleteViewElements(elements);

View File

@@ -46,4 +46,4 @@ for(i=0;i<el.text.length;i++) {
objectIDs.push(ea.addText(x,y,character));
}
ea.addToGroup(objectIDs);
ea.addElementsToView(true);
ea.addElementsToView(true, false, true);

70
ea-scripts/Text Aura.md Normal file
View File

@@ -0,0 +1,70 @@
/*
![](https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-aura.jpg)
Select a single text element, or a text element in a container. The container must have a transparent background.
The script will add an aura to the text by adding 4 copies of the text each with the inverted stroke color of the original text element and with a very small X and Y offset. The resulting 4 + 1 (original) text elements or containers will be grouped.
If you copy a color string on the clipboard before running the script, the script will use that color instead of the inverted color.
```js*/
els = ea.getViewSelectedElements();
const isText = (els.length === 1) && els[0].type === "text";
const isContainer = (els.length === 2) &&
((els[0].type === "text" && els[1].id === els[0].containerId && els[1].backgroundColor.toLowerCase() === "transparent") ||
(els[1].type === "text" && els[0].id === els[1].containerId && els[0].backgroundColor.toLowerCase() === "transparent"));
if (!(isText || isContainer)) {
new Notice ("Select a single text element, or a container with a text element and with transparent background color",10000);
return;
}
let strokeColor = ea
.getCM(els.filter(el=>el.type === "text")[0].strokeColor)
.invert({alpha: false})
.stringHEX({alpha: false});
clipboardText = await navigator.clipboard.readText();
if(clipboardText) {
const cm1 = ea.getCM(clipboardText);
if(cm1.format !== "invalid") {
strokeColor = cm1.stringHEX();
} else {
const cm2 = ea.getCM("#"+clipboardText);
if(cm2.format !== "invalid") {
strokeColor = cm2.stringHEX();
}
}
}
const offset = els.filter(el=>el.type === "text")[0].fontSize/24;
let ids = [];
const addClone = (offsetX, offsetY) => {
els.forEach(el=>{
const clone = ea.cloneElement(el);
ids.push(clone.id);
clone.x += offsetX;
clone.y += offsetY;
if(offsetX!==0 || offsetY!==0) {
switch (clone.type) {
case "text":
clone.strokeColor = strokeColor;
break;
default:
clone.strokeColor = "transparent";
break;
}
}
ea.elementsDict[clone.id] = clone;
})
}
addClone(-offset,0);
addClone(offset,0);
addClone(0,offset);
addClone(0,-offset);
addClone(0,0);
ea.copyViewElementsToEAforEditing(els);
els.forEach(el=>ea.elementsDict[el.id].isDeleted = true);
ea.addToGroup(ids);
ea.addElementsToView(false, true, true);

17
ea-scripts/Text Aura.svg Normal file
View File

@@ -0,0 +1,17 @@
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 120" width="120" height="120">
<!-- svg-source:excalidraw -->
<defs>
<style class="style-fonts">
@font-face {
font-family: "Virgil";
src: url("https://excalidraw.com/Virgil.woff2");
}
@font-face {
font-family: "Cascadia";
src: url("https://excalidraw.com/Cascadia.woff2");
}
</style>
</defs>
<g stroke-linecap="round"><g transform="translate(0 0) rotate(0 60 60)" fill-rule="evenodd"><path d="M0 0 L120 0 L120 40 L80 40 L80 120 L40 120 L40 40 L0 40 L0 0" stroke="none" stroke-width="0" fill="red" fill-rule="evenodd"></path><path d="M0 0 C41.51 0, 83.02 0, 120 0 M0 0 C30.58 0, 61.16 0, 120 0 M120 0 C120 12.11, 120 24.22, 120 40 M120 0 C120 13.92, 120 27.84, 120 40 M120 40 C108.75 40, 97.49 40, 80 40 M120 40 C107.65 40, 95.29 40, 80 40 M80 40 C80 66.51, 80 93.01, 80 120 M80 40 C80 70.33, 80 100.66, 80 120 M80 120 C66.08 120, 52.16 120, 40 120 M80 120 C70.07 120, 60.13 120, 40 120 M40 120 C40 89.21, 40 58.42, 40 40 M40 120 C40 92.66, 40 65.33, 40 40 M40 40 C25.35 40, 10.71 40, 0 40 M40 40 C27.7 40, 15.4 40, 0 40 M0 40 C0 24.03, 0 8.05, 0 0 M0 40 C0 27.82, 0 15.65, 0 0 M0 0 C0 0, 0 0, 0 0 M0 0 C0 0, 0 0, 0 0" stroke="transparent" stroke-width="0.5" fill="none"></path></g></g><mask></mask><g stroke-linecap="round"><g transform="translate(110 10) rotate(0 -50 50)" fill-rule="evenodd"><path d="M0 0 L-100 0 L-100 20 L-60 20 L-60 100 L-40 100 L-40 20 L0 20 L0 0" stroke="none" stroke-width="0" fill="currentColor" fill-rule="evenodd"></path><path d="M0 0 C-23.27 0, -46.54 0, -100 0 M0 0 C-23.31 0, -46.62 0, -100 0 M-100 0 C-100 6.13, -100 12.26, -100 20 M-100 0 C-100 5.84, -100 11.69, -100 20 M-100 20 C-87.37 20, -74.74 20, -60 20 M-100 20 C-88.34 20, -76.68 20, -60 20 M-60 20 C-60 37.78, -60 55.56, -60 100 M-60 20 C-60 39.34, -60 58.68, -60 100 M-60 100 C-52.58 100, -45.17 100, -40 100 M-60 100 C-54.72 100, -49.43 100, -40 100 M-40 100 C-40 83.83, -40 67.67, -40 20 M-40 100 C-40 77.76, -40 55.51, -40 20 M-40 20 C-25.4 20, -10.8 20, 0 20 M-40 20 C-28.47 20, -16.93 20, 0 20 M0 20 C0 15.42, 0 10.84, 0 0 M0 20 C0 14.54, 0 9.08, 0 0 M0 0 C0 0, 0 0, 0 0 M0 0 C0 0, 0 0, 0 0" stroke="currentColor" stroke-width="0.5" fill="none"></path></g></g><mask></mask></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

33
ea-scripts/Toggle Grid.md Normal file
View File

@@ -0,0 +1,33 @@
/*
Toggles the grid on and off. Especially useful when drawing with just a pen without a mouse or keyboard, as toggling the grid by left-clicking with the pen is sometimes quite tedious.
See documentation for more details:
https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.html
```javascript
*/
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.8.11")) {
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
return;
}
const api = ea.getExcalidrawAPI();
let {gridSize, previousGridSize} = api.getAppState();
if (!previousGridSize) {
previousGridSize = 20
}
if (!gridSize) {
gridSize = previousGridSize;
}
else
{
previousGridSize = gridSize;
gridSize = null;
}
ea.viewUpdateScene({
appState:{
gridSize,
previousGridSize
},
commitToHistory:false
});

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 567 489">
<path
d="M 20.803582,0.35478208 A 25,25 0 0 0 5.9442069,8.8176728 25,25 0 0 0 8.8172543,44.055954 L 31.254754,63.108689 c -0.121266,0.849954 -0.301322,1.680716 -0.388672,2.541015 -0.218469,2.151668 -0.330078,4.335551 -0.330078,6.544922 V 392.19462 c 0,2.20625 0.111609,4.38587 0.330078,6.53516 0.218468,2.14929 0.544482,4.26807 0.970703,6.34961 0.426219,2.08154 0.952918,4.12591 1.576172,6.12891 0.623252,2.00299 1.342775,3.96328 2.152343,5.87695 0.80957,1.91367 1.708193,3.7802 2.69336,5.59375 0.985166,1.81355 2.056984,3.57472 3.207031,5.27734 1.150047,1.70264 2.379385,3.34681 3.683594,4.92774 1.304208,1.58093 2.683206,3.09844 4.130859,4.54687 1.447654,1.44844 2.964542,2.82767 4.544922,4.13282 1.58038,1.30514 3.223392,2.53642 4.925781,3.6875 1.70239,1.15106 3.463664,2.22277 5.277344,3.20898 1.81368,0.98621 3.679496,1.88672 5.59375,2.69727 1.914254,0.81053 3.87675,1.5302 5.880859,2.15429 2.00411,0.6241 4.049565,1.15323 6.132813,1.58008 2.083248,0.42686 4.203801,0.75188 6.355469,0.9707 2.151667,0.21883 4.335552,0.33204 6.544922,0.33203 H 478.53601 c 2.20625,0 4.38587,-0.1132 6.53515,-0.33203 2.14929,-0.21882 4.26808,-0.54384 6.34961,-0.9707 0.30707,-0.063 0.59887,-0.16503 0.9043,-0.23242 l 33.48047,28.43164 a 25,25 0 0 0 35.23828,-2.87305 25,25 0 0 0 -2.87305,-35.23828 L 41.182488,5.9446259 A 25,25 0 0 0 29.485222,0.40556338 25,25 0 0 0 20.803582,0.35478208 Z M 94.536004,8.1946259 c -2.209366,0 -4.39326,0.1116097 -6.544922,0.3300781 -2.151664,0.2184684 -4.272226,0.5425319 -6.355469,0.9687499 -2.083244,0.42622 -4.128707,0.9548741 -6.132813,1.5781251 -2.004105,0.623253 -3.966609,1.340824 -5.880859,2.150391 -0.337447,0.142712 -0.651869,0.326303 -0.986328,0.474609 l 68.884767,58.498047 h 93.49024 23.52539 v 19.978516 79.392578 l 109.07422,92.6289 h 93.49218 21.4336 v 18.20313 79.39258 l 60.42383,51.31445 c 0.22119,-0.63745 0.49391,-1.25011 0.69531,-1.89648 0.62409,-2.00299 1.15127,-4.04738 1.57812,-6.12891 0.42686,-2.08153 0.75188,-4.20033 0.97071,-6.34961 0.21882,-2.14928 0.33203,-4.32892 0.33203,-6.53516 V 336.74736 271.15166 72.194626 c 0,-2.209349 -0.11321,-4.393275 -0.33203,-6.544922 -0.21882,-2.151647 -0.54386,-4.272242 -0.97071,-6.355469 -0.42685,-2.083227 -0.95403,-4.128722 -1.57812,-6.132812 -0.62409,-2.00409 -1.34376,-3.966624 -2.1543,-5.88086 -0.81054,-1.914234 -1.71302,-3.780086 -2.69922,-5.59375 -0.98619,-1.813662 -2.05792,-3.574971 -3.20898,-5.277343 -1.15106,-1.702373 -2.38041,-3.34737 -3.68555,-4.927735 -1.30514,-1.580364 -2.68439,-3.097282 -4.13281,-4.544922 -1.44842,-1.447638 -2.96596,-2.82471 -4.54688,-4.128906 -1.5809,-1.304195 -3.22708,-2.535511 -4.92968,-3.685547 -1.70262,-1.150034 -3.46187,-2.219921 -5.27539,-3.205078 -1.81353,-0.985156 -3.68011,-1.885752 -5.59375,-2.695312 -1.91366,-0.809561 -3.87593,-1.527143 -5.87891,-2.150391 -2.00298,-0.623246 -4.04739,-1.1519091 -6.12891,-1.5781251 -2.08151,-0.426215 -4.20034,-0.7502832 -6.34961,-0.9687499 -2.14925,-0.2184666 -4.32893,-0.3300781 -6.53515,-0.3300781 H 232.88952 155.64538 Z M 318.53601,72.194626 h 160 V 200.19462 H 458.97937 381.73718 318.53601 V 146.52275 80.927048 Z M 94.536004,116.84892 192.67859,200.19462 H 94.536004 Z m 0,147.3457 H 254.53601 v 128 H 94.536004 Z m 224.000006,42.87891 100.23437,85.12109 H 318.53601 Z" />
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because one or more lines are too long

View File

@@ -15,38 +15,26 @@ In the `Command Palette` installed scripts are prefixed with `Downloaded/`, thus
## 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).
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 available (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)
- An update to this file [ea-scripts/index-new.md](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/index-new.md)
---
# List of available scripts
## Layout and Organization
**Keywords**: Design, Placement, Arrangement, Structure, Formatting, Alignment
| | |
|----|-----|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Connector%20Point.svg"></div>|[[#Add Connector Point]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Link%20to%20Existing%20File%20and%20Open.svg"/></div>|[[#Add Link to Existing File and Open]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Link%20to%20New%20Page%20and%20Open.svg"/></div>|[[#Add Link to New Page and Open]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Next%20Step%20in%20Process.svg"/></div>|[[#Add Next Step in Process]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Auto%20Draw%20for%20Pen.svg"/></div>|[[#Auto Draw for Pen]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Auto%20Layout.svg"/></div>|[[#Auto Layout]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Box%20Each%20Selected%20Groups.svg"/></div>|[[#Box Each Selected Groups]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Box%20Selected%20Elements.svg"/></div>|[[#Box Selected Elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Change%20shape%20of%20selected%20elements.svg"/></div>|[[#Change shape of selected elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Connect%20elements.svg"/></div>|[[#Connect elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20freedraw%20to%20line.svg"/></div>|[[#Convert freedraw to line]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20selected%20text%20elements%20to%20sticky%20notes.svg"/></div>|[[#Convert selected text elements to sticky notes]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20text%20to%20link%20with%20folder%20and%20alias.svg"/></div>|[[#Convert text to link with folder and alias]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Copy%20Selected%20Element%20Styles%20to%20Global.svg"/></div>|[[#Copy Selected Element Styles to Global]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Create%20DrawIO%20file.svg"/></div>|[[#Create DrawIO file]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Create%20new%20markdown%20file%20and%20embed%20into%20active%20drawing.svg"/></div>|[[#Create new markdown file and embed into active drawing]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Darken%20background%20color.svg"/></div>|[[#Darken background color]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Deconstruct%20selected%20elements%20into%20new%20drawing.svg"/></div>|[[#Deconstruct selected elements into new drawing]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Elbow%20connectors.svg"/></div>|[[#Elbow connectors]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Excalidraw%20Collaboration%20Frame.svg"/></div>|[[#Excalidraw Collaboration Frame]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Ellipse%20Selected%20Elements.svg"/></div>|[[#Ellipse Selected Elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20horizontally%20keep%20text%20centered.svg"/></div>|[[#Expand rectangles horizontally keep text centered]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20horizontally.svg"/></div>|[[#Expand rectangles horizontally]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Expand%20rectangles%20vertically%20keep%20text%20centered.svg"/></div>|[[#Expand rectangles vertically keep text centered]]|
@@ -56,37 +44,119 @@ I would love to include your contribution in the script library. If you have a s
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20spacing.svg"/></div>|[[#Fixed spacing]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20vertical%20distance%20between%20centers.svg"/></div>|[[#Fixed vertical distance between centers]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Fixed%20vertical%20distance.svg"/></div>|[[#Fixed vertical distance]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Folder%20Note%20Core%20-%20Make%20Current%20Drawing%20a%20Folder.svg"/></div>|[[#Folder Note Core - Make Current Drawing a Folder]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Golden%20Ratio.svg"/></div>|[[#Golden Ratio]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Grid%20Selected%20Images.svg"/></div>|[[#Grid selected images]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Hardware%20Eraser%20Support.svg"/></div>|[[#Hardware Eraser Support]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Mindmap%20format.svg"/></div>|[[#Mindmap format]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.svg"/></div>|[[#Zoom to Fit Selected Elements]]|
## Connectors and Arrows
**Keywords**: Links, Relations, Paths, Direction, Flow, Connections
| | |
|----|-----|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Connector%20Point.svg"></div>|[[#Add Connector Point]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Concatenate%20lines.svg"></div>|[[#Concatenate lines]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Connect%20elements.svg"/></div>|[[#Connect elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Elbow%20connectors.svg"/></div>|[[#Elbow connectors]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Mindmap%20connector.svg"/></div>|[[#Mindmap connector]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Normalize%20Selected%20Arrows.svg"/></div>|[[#Normalize Selected Arrows]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Reverse%20arrows.svg"/></div>|[[#Reverse arrows]]|
## Text Manipulation
**Keywords**: Editing, Font Control, Wording, Typography, Annotation, Modification
| | |
|----|-----|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20selected%20text%20elements%20to%20sticky%20notes.svg"/></div>|[[#Convert selected text elements to sticky notes]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Relative%20Font%20Size%20Cycle.svg"/></div>|[[#Relative Font Size Cycle]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Scribble%20Helper.svg"/></div>|[[#Scribble Helper]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Font%20Family.svg"/></div>|[[#Set Font Family]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Text%20Alignment.svg"/></div>|[[#Set Text Alignment]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Split%20text%20by%20lines.svg"/></div>|[[#Split text by lines]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Text%20Arch.svg"/></div>|[[#Text Arch]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Text%20Aura.svg"/></div>|[[#Text Aura]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Text%20to%20Sticky%20Notes.svg"/></div>|[[#Text to Sticky Notes]]|
## Styling and Appearance
**Keywords**: Design, Look, Visuals, Graphics, Aesthetics, Presentation
| | |
|----|-----|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Change%20shape%20of%20selected%20elements.svg"/></div>|[[#Change shape of selected elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Darken%20background%20color.svg"/></div>|[[#Darken background color]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Invert%20colors.svg"/></div>|[[#Invert colors]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Lighten%20background%20color.svg"/></div>|[[#Lighten background color]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Mindmap%20connector.svg"/></div>|[[#Mindmap connector]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Mindmap%20format.svg"/></div>|[[#Mindmap format]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Modify%20background%20color%20opacity.svg"/></div>|[[#Modify background color opacity]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Normalize%20Selected%20Arrows.svg"/></div>|[[#Normalize Selected Arrows]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Organic%20Line.svg"/></div>|[[#Organic Line]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Organic%20Line%20Legacy.svg"/></div>|[[#Organic Line Legacy]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Reset%20LaTeX%20Size.svg"/></div>|[[#Reset LaTeX Size]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.svg"/></div>|[[#Set background color of unclosed line object by adding a shadow clone]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Dimensions.svg"/></div>|[[#Set Dimensions]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Grid.svg"/></div>|[[#Set Grid]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Stroke%20Width%20of%20Selected%20Elements.svg"/></div>|[[#Set Stroke Width of Selected Elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Toggle%20Grid.svg"/></div>|[[#Toggle Grid]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Uniform%20size.svg"/></div>|[[#Uniform Size]]|
## Linking and Embedding
**Keywords**: Attach, Incorporate, Integrate, Associate, Insert, Reference
| | |
|----|-----|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Link%20to%20Existing%20File%20and%20Open.svg"/></div>|[[#Add Link to Existing File and Open]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Link%20to%20New%20Page%20and%20Open.svg"/></div>|[[#Add Link to New Page and Open]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20text%20to%20link%20with%20folder%20and%20alias.svg"/></div>|[[#Convert text to link with folder and alias]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Create%20DrawIO%20file.svg"/></div>|[[#Create DrawIO file]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Create%20new%20markdown%20file%20and%20embed%20into%20active%20drawing.svg"/></div>|[[#Create new markdown file and embed into active drawing]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Folder%20Note%20Core%20-%20Make%20Current%20Drawing%20a%20Folder.svg"/></div>|[[#Folder Note Core - Make Current Drawing a Folder]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Link%20Alias.svg"/></div>|[[#Set Link Alias]]|
## Utilities and Tools
**Keywords**: Functionalities, Instruments, Helpers, Aids, Features, Enhancements
| | |
|----|-----|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Boolean%20Operations.svg"/></div>|[[#Boolean Operations]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Custom%20Zoom.svg"/></div>|[[#Custom Zoom]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Copy%20Selected%20Element%20Styles%20to%20Global.svg"/></div>|[[#Copy Selected Element Styles to Global]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/ExcaliAI.svg"/></div>|[[#ExcaliAI]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Excalidraw%20Writing%20Machine.svg"/></div>|[[#Excalidraw Writing Machine]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/GPT-Draw-a-UI.svg"/></div>|[[#GPT Draw-a-UI]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Palette%20loader.svg"/></div>|[[#Palette Loader]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/PDF%20Page%20Text%20to%20Clipboard.svg"/></div>|[[#PDF Page Text to Clipboard]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Rename%20Image.svg"/></div>|[[#Rename Image]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Repeat%20Elements.svg"/></div>|[[#Repeat Elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Reverse%20arrows.svg"/></div>|[[#Reverse arrows]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Scribble%20Helper.svg"/></div>|[[#Scribble Helper]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Repeat%20Texts.svg"/></div>|[[#Repeat Texts]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Select%20Elements%20of%20Type.svg"/></div>|[[#Select Elements of Type]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.svg"/></div>|[[#Set background color of unclosed line object by adding a shadow clone]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Dimensions.svg"/></div>|[[#Set Dimensions]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Font%20Family.svg"/></div>|[[#Set Font Family]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Grid.svg"/></div>|[[#Set Grid]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Link%20Alias.svg"/></div>|[[#Set Link Alias]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Stroke%20Width%20of%20Selected%20Elements.svg"/></div>|[[#Set Stroke Width of Selected Elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20Text%20Alignment.svg"/></div>|[[#Set Text Alignment]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Select%20Similar%20Elements.svg"/></div>|[[#Select Similar Elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Slideshow.svg"/></div>|[[#Slideshow]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Split%20text%20by%20lines.svg"/></div>|[[#Split text by lines]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Text%20Arch.svg"/></div>|[[#Text Arch]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Text%20to%20Sticky%20Notes.svg"/></div>|[[#Text to Sticky Notes]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Uniform%20size.svg"/></div>|[[#Uniform Size]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.svg"/></div>|[[#Zoom to Fit Selected Elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Split%20Ellipse.svg"/></div>|[[#Split Ellipse]]|
## Collaboration and Export
**Keywords**: Sharing, Teamwork, Exporting, Distribution, Cooperative, Publish
| | |
|----|-----|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Excalidraw%20Collaboration%20Frame.svg"/></div>|[[#Excalidraw Collaboration Frame]]|
## Conversation and Creation
**Keywords**: Transform, Generate, Craft, Produce, Change, Originate
| | |
|----|-----|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Add%20Next%20Step%20in%20Process.svg"/></div>|[[#Add Next Step in Process]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Convert%20freedraw%20to%20line.svg"/></div>|[[#Convert freedraw to line]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Deconstruct%20selected%20elements%20into%20new%20drawing.svg"/></div>|[[#Deconstruct selected elements into new drawing]]|
## Masking and cropping
**Keywords**: Crop, Mask, Transform images
| | |
|----|-----|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Crop%20Vintage%20Mask.svg"/></div>|[[#Crop Vintage Mask]]|
---
# Description and Installation
## Add Connector Point
```excalidraw-script-install
@@ -112,24 +182,19 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Add%20Next%20Step%20in%20Process.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will prompt you for the title of the process step, then will create a stick note with the text. If an element is selected then the script will connect this new step with an arrow to the previous step (the selected element). If no element is selected, then the script assumes this is the first step in the process and will only output the sticky note with the text that was entered.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-add-process-step.jpg'></td></tr></table>
## Alternative Pens
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Alternative%20Pens.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/Alternative%20Pens.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will load pen presets overriding the default freedraw line in Excalidraw. Once you've downloaded this script, check the script description for a detailed how to guide.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-alternative-pens.jpg'></td></tr></table>
## Auto Draw for Pen
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Auto%20Draw%20for%20Pen.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/threethan'>@threethan</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/Auto%20Draw%20for%20Pen.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Automatically switches from select mode to drawing mode when hovering a pen, and then back.</td></tr></table>
## Auto Layout
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Auto%20Layout.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/Auto%20Layout.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script performs automatic layout for the selected top-level grouping objects. It is powered by <a href='https://github.com/kieler/elkjs'>elkjs</a> and needs to be connected to the Internet.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-auto-layout.png'></td></tr></table>
## Boolean Operations
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Boolean%20Operations.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/GColoy'>@GColoy</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/Boolean%20Operations.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">With This Script it is possible to make boolean Operations on Shapes.<br>The style of the resulting shape will be the style of the highest ranking Element that was used.<br>The ranking of the elements is based on their background. The "denser" the background, the higher the ranking (the order of backgroundstyles is shown below). If they have the same background the opacity will decide. If thats also the same its decided by the order they were created.<br>The ranking is also important for the difference operation, so a transparent object for example will cut a hole into a solid object.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-boolean-operations-showcase.png'><br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-boolean-operations-element-ranking.png'></td></tr></table>
## Box Each Selected Groups
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Box%20Each%20Selected%20Groups.md
@@ -148,6 +213,12 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Change%20shape%20of%20selected%20elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script allows you to change the shape and fill style of selected Rectangles, Diamonds, Ellipses, Lines, Arrows and Freedraw.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-change-shape.jpg'></td></tr></table>
## Concatenate lines
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Concatenate%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/Concatenate%20lines.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-concatenate-lines.png'></td></tr></table>
## Connect elements
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Connect%20elements.md
@@ -190,6 +261,18 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/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>
## Crop Vintage Mask
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Crop%20Vintage%20Mask.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/Crop%20Vintage%20Mask.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Adds a rounded mask to the image by adding a full cover black mask and a rounded rectangle white mask. The script is also useful for adding just a black mask. In this case, run the script, then delete the white mask and add your custom white mask.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-crop-vintage.jpg'></td></tr></table>
## Custom Zoom
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Custom%20Zoom.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/Custom%20Zoom.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">You can set a custom zoom level with this script. This allows you to set a zoom level below 10% or set the zoom level to a specific value. Note however, that Excalidraw has a bug under 10% zoom... a phantom copy of your image may appear on screen. If this happens, increase the zoom and the phantom should disappear, if it doesn't, then close and open the drawing.</td></tr></table>
## Darken background color
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Darken%20background%20color.md
@@ -200,7 +283,7 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Deconstruct%20selected%20elements%20into%20new%20drawing.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Deconstruct%20selected%20elements%20into%20new%20drawing.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Select some elements in the scene. The script will take these elements and move them into a new Excalidraw file, and open that file. The selected elements will also be replaced in your original drawing with the embedded Excalidraw file (the one that was just created). You will be prompted for the file name of the new deconstructed image. The script is useful if you want to break a larger drawing into smaller reusable parts that you want to reference in multiple drawings.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-deconstruct.jpg'></td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Deconstruct%20selected%20elements%20into%20new%20drawing.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Select some elements in the scene. The script will take these elements and move them into a new Excalidraw file, and open that file. The selected elements will also be replaced in your original drawing with the embedded Excalidraw file (the one that was just created). You will be prompted for the file name of the new deconstructed image. The script is useful if you want to break a larger drawing into smaller reusable parts that you want to reference in multiple drawings.<br><iframe width="400" height="225" src="https://www.youtube.com/embed/HRtaaD34Zzg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-deconstruct.jpg'></td></tr></table>
## Elbow connectors
```excalidraw-script-install
@@ -208,6 +291,12 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/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>
## Ellipse Selected Elements
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Ellipse%20Selected%20Elements.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/mazurov'>@mazurov</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/Ellipse%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 ellipse around the currently selected elements in Excalidraw.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-ellipse-elements.png'></td></tr></table>
## Excalidraw Collaboration Frame
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Excalidraw%20Collaboration%20Frame.md
@@ -274,17 +363,37 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Folder%20Note%20Core%20-%20Make%20Current%20Drawing%20a%20Folder.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script adds the `Folder Note Core: Make current document folder note` function to Excalidraw drawings. Running this script will convert the active Excalidraw drawing into a folder note. If you already have embedded images in your drawing, those attachments will not be moved when the folder note is created. You need to take care of those attachments separately, or convert the drawing to a folder note prior to adding the attachments. The script requires the <a href="https://github.com/aidenlx/folder-note-core" target="_blank">Folder Note Core</a> plugin.</td></tr></table>
## Golden Ratio
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Golden%20Ratio.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/Golden%20Ratio.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script performs two different functions depending on the elements selected in the view.<br>
1) In case you select text elements, the script will cycle through a set of font scales. First the 2 larger fonts following the Fibonacci sequence (fontsize * φ; fonsize * φ^2), then the 2 smaller fonts (fontsize / φ; fontsize / φ^2), finally the original size, followed again by the 2 larger fonts. If you wait 2 seconds, the sequence clears and starts from which ever font size you are on. So if you want the 3rd larges font, then toggle twice, wait 2 sec, then toggle again.<br>
2) In case you select a single rectangle, the script will open the "Golden Grid", "Golden Spiral" window, where you can set up the type of grid or spiral you want to insert into the document.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/golden-ratio.jpg'><br><iframe width="400" height="225" src="https://www.youtube.com/embed/2SHn_ruax-s" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></td></tr></table>
## Grid selected images
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Grid%20Selected%20Images.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/7flash'>@7flash</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%20Selected%20Images.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script arranges selected images into compact grid view, removing gaps in-between, resizing when necessary and breaking into multiple rows/columns.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-grid-selected-images.png'></td></tr></table>
## Hardware Eraser Support
## ExcaliAI
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Hardware%20Eraser%20Support.md
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/ExcaliAI.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/threethan'>@threethan</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/Hardware%20Eraser%20Support.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Allows you to use inversion, aka hardware eraser, on supported pens.</td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/ExcaliAI.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Various AI features based on GPT Vision.<br><iframe width="400" height="225" src="https://www.youtube.com/embed/A1vrSGBbWgo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-draw-a-ui.jpg'></td></tr></table>
## Excalidraw Writing Machine
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Excalidraw%20Writing%20Machine.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/Excalidraw%20Writing%20Machine.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Creates a hierarchical Markdown document out of a visual layout of an article that can be fed to Templater and converted into an article using AI for Templater.<br>Watch this video to understand how the script is intended to work:<br><iframe width="400" height="225" src="https://www.youtube.com/embed/zvRpCOZAUSs" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><br>You can download the sample Obsidian Templater file from <a href="https://gist.github.com/zsviczian/bf49d4b2d401f5749aaf8c2fa8a513d9">here</a>. You can download the demo PDF document showcased in the video from <a href="https://zsviczian.github.io/DemoArticle-AtomicHabits.pdf">here</a>.</td></tr></table>
## GPT Draw-a-UI
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/GPT-Draw-a-UI.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/GPT-Draw-a-UI.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script was discontinued in favor of ExcaliAI. Draw a UI and let GPT create the code for you.<br><iframe width="400" height="225" src="https://www.youtube.com/embed/y3kHl_6Ll4w" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-draw-a-ui.jpg'></td></tr></table>
## Invert colors
```excalidraw-script-install
@@ -326,7 +435,13 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Organic%20Line.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/Organic%20Line.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Converts selected freedraw lines such that pencil pressure will decrease from maximum to minimum from the beginning of the line to its end. The resulting line is placed at the back of the layers, under all other items. Helpful when drawing organic mindmaps.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-organic-line.jpg'></td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Organic%20Line.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Converts selected freedraw lines such that pencil pressure will decrease from maximum to minimum from the beginning of the line to its end. The resulting line is placed at the back of the layers, under all other items. Helpful when drawing organic mindmaps.<br>The script has been superseded by Custom Pens that you can enable in plugin settings. Find out more by watching this <a href="https://youtu.be/OjNhjaH2KjI" target="_blank">video</a><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-organic-line.jpg'></td></tr></table>
## Organic Line Legacy
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Organic%20Line%20Legacy.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/Organic%20Line%20Legacy.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Converts selected freedraw lines such that pencil pressure will decrease from maximum to minimum from the beginning of the line to its end. The resulting line is placed at the back of the layers, under all other items. Helpful when drawing organic mindmaps.<br>This is the old script from this <a href="https://youtu.be/JMcNDdj_lPs?t=479" target="_blank">video</a>. Since it's release this has been superseded by custom pens that you can enable in plugin settings. For more on custom pens, watch <a href="https://youtu.be/OjNhjaH2KjI" target="_blank">this</a><br>The benefit of the approach in this implementation of custom pens is that it will look the same on excalidraw.com when you copy your drawing over for sharing with non-Obsidian users. Otherwise custom pens are faster to use and much more configurable.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-organic-line-legacy.jpg'></td></tr></table>
## Palette Loader
```excalidraw-script-install
@@ -340,6 +455,12 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/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/PDF%20Page%20Text%20to%20Clipboard.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Copies the text from the selected PDF page on the Excalidraw canvas to the clipboard.<br><iframe width="400" height="225" src="https://www.youtube.com/embed/Kwt_8WdOUT4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><br><a href='https://youtu.be/Kwt_8WdOUT4' target='_blank'>Link to video on YouTube</a></td></tr></table>
## Relative Font Size Cycle
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Relative%20Font%20Size%20Cycle.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/Relative%20Font%20Size%20Cycle.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script will cycle through S, M, L, XL font sizes scaled to the current canvas zoom.</td></tr></table>
## Rename Image
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Rename%20Image.md
@@ -352,6 +473,12 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/1-2-3'>@1-2-3</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Repeat%20Elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script will detect the difference between 2 selected elements, including position, size, angle, stroke and background color, and create several elements that repeat these differences based on the number of repetitions entered by the user.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-repeat-elements.png'></td></tr></table>
## Repeat Texts
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Repeat%20Texts.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/soraliu'>@soraliu</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/Repeat%20Texts.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">In the following script, we address the concept of repetition through the lens of numerical progression. As visualized by the image, where multiple circles each labeled with an even task number are being condensed into a linear sequence, our script will similarly iterate through a set of numbers</td></tr></table>
## Reverse arrows
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Reverse%20arrows.md
@@ -362,7 +489,7 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Scribble%20Helper.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/Scribble%20Helper.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">iOS scribble helper for better handwriting experience with text elements. If no elements are selected then the creates a text element at pointer position and you can use the edit box to modify the text with scribble. If a text element is selected then opens the input prompt where you can modify this text with scribble.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-scribble-helper.jpg'></td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Scribble%20Helper.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">iOS scribble helper for better handwriting experience with text elements. If no elements are selected then the creates a text element at pointer position and you can use the edit box to modify the text with scribble. If a text element is selected then opens the input prompt where you can modify this text with scribble.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-scribble-helper.jpg'><br><iframe width="560" height="315" src="https://www.youtube.com/embed/BvYkOaly-QM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></td></tr></table>
## Select Elements of Type
```excalidraw-script-install
@@ -370,6 +497,20 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Select%20Elements%20of%20Type.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Prompts you with a list of the different element types in the active image. Only elements of the selected type will be selected on the canvas. If nothing is selected when running the script, then the script will process all the elements on the canvas. If some elements are selected when the script is executed, then the script will only process the selected elements.<br>The script is useful when, for example, you want to bring to front all the arrows, or want to change the color of all the text elements, etc.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-select-element-of-type.jpg'></td></tr></table>
## Select Similar Elements
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Select%20Similar%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/Select%20Similar%20Elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script allows you to streamline your Obsidian-Excalidraw workflows by enabling the selection of elements based on similar properties. you can precisely define which attributes such as stroke color, fill style, font family, and more, should match for selection. It's perfect for large canvases where manual selection would be cumbersome. You can either run the script to find and select matching elements across the entire scene, or define a specific group of elements to apply the selection criteria within a defined timeframe.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-select-similar-elements.png'></td></tr></table>
## Reset LaTeX Size
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Reset%20LaTeX%20Size.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/firai'>@firai</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/Reset%20LaTeX%20Size.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Reset the sizes of embedded LaTeX equations to the default sizes or a multiple of the default sizes.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-reset-latex.jpg'></td></tr></table>
## Set background color of unclosed line object by adding a shadow clone
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Set%20background%20color%20of%20unclosed%20line%20object%20by%20adding%20a%20shadow%20clone.md
@@ -416,7 +557,13 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Slideshow.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/Slideshow.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script will convert your drawing into a slideshow presentation.<br><iframe width="560" height="315" src="https://www.youtube.com/embed/HhRHFhWkmCk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Slideshow.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">The script will convert your drawing into a slideshow presentation.<br><iframe width="560" height="315" src="https://www.youtube.com/embed/JwgtCrIVeEU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></td></tr></table>
## Split Ellipse
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Split%20Ellipse.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/GColoy'>@GColoy</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%20Ellipse.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script splits an ellipse at any point where a line intersects it. If no lines are selected, it will use every line that intersects the ellipse. Otherwise, it will only use the selected lines. If there is no intersecting line, the ellipse will be converted into a line object.<br>There is also the option to close the object along the cut, which will close the cut in the shape of the line.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-splitEllipse-demo1.png'><br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-splitEllipse-demo2.png'><br>Tip: To use an ellipse as the cutting object, you first have to use this script on it, since it will convert the ellipse into a line.</td></tr></table>
## Split text by lines
```excalidraw-script-install
@@ -430,6 +577,18 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Text%20Arch.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Fit a text to the arch of a circle. The script will prompt you for the radius of the circle and then split your text to individual letters and place each letter to the arch defined by the radius. Setting a lower radius value will increase the arching of the text. Note that the arched-text will no longer be editable as a text element and it will no longer function as a markdown link. Emojis are currently not supported.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/text-arch.jpg'></td></tr></table>
## Text Aura
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Text%20Aura.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%20Aura.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Select a single text element, or a text element in a container. The container must have a transparent background.<br>The script will add an aura to the text by adding 4 copies of the text each with the inverted stroke color of the original text element and with a very small X and Y offset. The resulting 4 + 1 (original) text elements or containers will be grouped.<br>If you copy a color string on the clipboard before running the script, the script will use that color instead of the inverted color.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-text-aura.jpg'></td></tr></table>
## Toggle Grid
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Toggle%20Grid.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/GColoy'>@GColoy</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/Toggle%20Grid.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Toggles the grid on and off.<br> Especially useful when drawing with just a pen without a mouse or keyboard, as toggling the grid by left-clicking with the pen is sometimes quite tedious.</table>
## Text to Sticky Notes
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Text%20to%20Sticky%20Notes.md
@@ -446,4 +605,4 @@ https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Similar to Excalidraw standard <kbd>SHIFT+2</kbd> feature: Zoom to fit selected elements, but with the ability to zoom to 1000%. Inspiration: [#272](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/272)</td></tr></table>
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/zsviczian'>@zsviczian</a></td></tr><tr valign='top'><td class="label">Source</td><td class="data"><a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Zoom%20to%20Fit%20Selected%20Elements.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Similar to Excalidraw standard <kbd>SHIFT+2</kbd> feature: Zoom to fit selected elements, but with the ability to zoom to 1000%. Inspiration: [#272](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/272)</td></tr></table>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 KiB

After

Width:  |  Height:  |  Size: 861 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
images/golden-ratio.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
images/scripts-repeat-texts.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
images/vintage-mask.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

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

View File

@@ -1,11 +1,12 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "1.9.8",
"version": "2.6.4",
"minAppVersion": "1.1.6",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",
"authorUrl": "https://zsolt.blog",
"authorUrl": "https://www.zsolt.blog",
"fundingUrl": "https://ko-fi.com/zsolt",
"helpUrl": "https://github.com/zsviczian/obsidian-excalidraw-plugin#readme",
"isDesktopOnly": false
}

View File

@@ -1,6 +1,6 @@
{
"name": "obsidian-excalidraw-plugin",
"version": "1.8.10",
"version": "2.2.5",
"description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@@ -10,61 +10,74 @@
"scripts": {
"dev": "cross-env NODE_ENV=development rollup --config rollup.config.js -w",
"build": "cross-env NODE_ENV=production rollup --config rollup.config.js",
"lib": "cross-env NODE_ENV=lib rollup --config rollup.config.js -w",
"code:fix": "eslint --max-warnings=0 --ext .ts,.tsx ./src --fix"
"lib": "cross-env NODE_ENV=lib rollup --config rollup.config.js",
"code:fix": "eslint --max-warnings=0 --ext .ts,.tsx ./src --fix",
"madge": "madge --circular ."
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@types/lz-string": "^1.3.34",
"@zsviczian/excalidraw": "0.15.2-obsidian-7",
"@popperjs/core": "^2.11.8",
"@zsviczian/excalidraw": "0.17.6-11",
"chroma-js": "^2.4.2",
"clsx": "^1.2.1",
"colormaster": "^1.2.1",
"clsx": "^2.0.0",
"@zsviczian/colormaster": "^1.2.2",
"gl-matrix": "^3.4.3",
"lz-string": "^1.4.4",
"js-yaml": "^4.1.0",
"lucide-react": "^0.263.1",
"mathjax-full": "^3.2.2",
"monkey-around": "^2.3.0",
"nanoid": "^4.0.2",
"opentype.js": "^1.3.4",
"polybooljs": "^1.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "^5.0.1",
"roughjs": "^4.5.2",
"html2canvas": "^1.4.1",
"@popperjs/core": "^2.11.6",
"nanoid": "^4.0.0",
"lucide-react": "0.259.0"
"woff2sfnt-sfnt2woff": "^1.0.0",
"es6-promise-pool": "2.5.0"
},
"devDependencies": {
"@babel/core": "^7.20.12",
"@babel/preset-env": "^7.20.2",
"@babel/preset-react": "^7.18.6",
"jsesc": "^3.0.2",
"@babel/core": "^7.22.9",
"@babel/preset-env": "^7.22.10",
"@babel/preset-react": "^7.22.5",
"@codemirror/commands": "^6.3.3",
"@codemirror/language": "^6.10.0",
"@codemirror/search": "^6.5.5",
"@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.23.0",
"@excalidraw/eslint-config": "^1.0.3",
"@excalidraw/prettier-config": "^1.0.2",
"@rollup/plugin-babel": "^6.0.3",
"@rollup/plugin-commonjs": "^24.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/plugin-typescript": "^11.0.0",
"@types/chroma-js": "^2.1.4",
"@types/js-beautify": "^1.13.3",
"@types/node": "^18.11.18",
"@types/react-dom": "^18.0.10",
"@rollup/plugin-typescript": "^11.1.6",
"@types/chroma-js": "^2.4.0",
"@types/js-beautify": "^1.14.0",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.10.5",
"@types/opentype.js": "^1.3.8",
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18",
"@zerollup/ts-transform-paths": "^1.7.18",
"cross-env": "^7.0.3",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-prettier": "^4.2.1",
"obsidian": "^1.1.1",
"prettier": "^2.8.2",
"cssnano": "^6.0.2",
"dotenv": "^16.4.5",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"lz-string": "^1.5.0",
"obsidian": "1.5.7-1",
"prettier": "^3.0.1",
"rollup": "^2.70.1",
"rollup-plugin-copy": "^3.4.0",
"rollup-plugin-postprocess": "github:brettz9/rollup-plugin-postprocess#update",
"rollup-plugin-copy": "^3.5.0",
"@zsviczian/rollup-plugin-postprocess": "^1.0.3",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.34.1",
"rollup-plugin-visualizer": "^5.9.0",
"rollup-plugin-web-worker-loader": "^1.6.1",
"tslib": "^2.4.1",
"tslib": "^2.6.1",
"ttypescript": "^1.5.15",
"typescript": "^4.9.4"
"typescript": "^5.2.2"
},
"resolutions": {
"@typescript-eslint/typescript-estree": "5.3.0"

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