Compare commits
48 Commits
1.9.14
...
1.9.19-mer
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13e4203c44 | ||
|
|
d04b628d55 | ||
|
|
2eab13661d | ||
|
|
6eefb49eb0 | ||
|
|
6878b1f969 | ||
|
|
0ba784564f | ||
|
|
62804cdc63 | ||
|
|
ff900b4b61 | ||
|
|
e34f03c0d1 | ||
|
|
2a9cf1cf6b | ||
|
|
d4159dbe75 | ||
|
|
7cd68304c3 | ||
|
|
0d7a76310c | ||
|
|
056be574ef | ||
|
|
f6e2886185 | ||
|
|
e4d05ac284 | ||
|
|
5311f53612 | ||
|
|
f20ce396f0 | ||
|
|
996f1f79f1 | ||
|
|
cd58d1af06 | ||
|
|
9d9201b4d1 | ||
|
|
684b2f6268 | ||
|
|
de08f4584d | ||
|
|
a937b5d70a | ||
|
|
95b99edede | ||
|
|
cdf7591e0f | ||
|
|
f373867356 | ||
|
|
041efcab74 | ||
|
|
e1fe3eeaab | ||
|
|
3793148c77 | ||
|
|
5dbfeb085e | ||
|
|
f79181c76a | ||
|
|
c0df46cb7b | ||
|
|
aa7dcf7604 | ||
|
|
fe4a39afc5 | ||
|
|
4185192954 | ||
|
|
3a9ee63c97 | ||
|
|
d2d2537867 | ||
|
|
97fe819737 | ||
|
|
9fdca28579 | ||
|
|
261e093700 | ||
|
|
07a651c2c8 | ||
|
|
6c0a1f9a4d | ||
|
|
9d941a4e44 | ||
|
|
cbb8f676af | ||
|
|
3bcf460ce4 | ||
|
|
23dd4883e3 | ||
|
|
ce2e0fd408 |
@@ -1,4 +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:
|
||||
|
||||
|
||||
270
README.md
@@ -62,51 +62,46 @@ The Obsidian-Excalidraw plugin integrates [Excalidraw](https://excalidraw.com/),
|
||||
|
||||
## 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.
|
||||
- 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.
|
||||
- Template for new drawings. The template will restore stroke properties. This means you can set up defaults in your template for stroke color, stroke width, opacity, font family, font size, fill style, stroke style, etc. This also applies to ExcalidrawAutomate.
|
||||
- Via the template, you can customize the color palette used by Excalidraw.
|
||||
- Switch to Markdown view.
|
||||
- Scroll down to the bottom of the file and find `"AppState": {`.
|
||||
- Find `"customColorPalette": {` at the end of the AppState section.
|
||||
- You may specify the 3 palettes used in Excalidraw by adding any or all of the following 3 variables:
|
||||
- `"canvasBackground":[], "elementBackground":[], "elementStroke": []`.
|
||||
- Add a comma-separated list of valid HTML colors (e.g. `#FF0000` for red).
|
||||
in the array for each of the variables.
|
||||
- See my videos above for further help.
|
||||
|
||||
#### Export
|
||||
|
||||
- If portability is important to you:
|
||||
- Auto-export SVG and/or PNG files including keep-in-sync feature so you can
|
||||
embed SVG/PNG into your documents instead of embedding excalidraw files.
|
||||
- You can override export settings for an individual file by adding the `excalidraw-autoexport`
|
||||
frontmatter key. Valid values for this key are `none`, `both`, `png` and `svg`.
|
||||
- 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 +109,144 @@ 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
|
||||
|
||||

|
||||
|
||||
#### 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.
|
||||
|
||||
### 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 freedraw pens. See documentation [here].(https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Alternative%20Pens.md), [video](https://youtu.be/uZz5MgzWXiM)
|
||||
|
||||
### 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](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 the demo [video](https://youtu.be/wTtaXmRJ7wg?t=16).
|
||||
|
||||
### Other
|
||||
|
||||
- Left-handed mode
|
||||
- Includes full
|
||||
- [QuickAdd](https://github.com/chhoumann/quickadd),
|
||||
@@ -269,7 +271,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 +286,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>
|
||||
|
||||
361
ea-scripts/Boolean Operations.md
Normal file
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
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 elemtns 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 diffrence operation, so a tranparent object for example will cut a hole into a solid object.
|
||||

|
||||

|
||||
|
||||
|
||||
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 PolyBool = ea.getPolybool();
|
||||
const polyboolAction = await utils.suggester(["union (a + b)", "intersect (a && b)", "diffrence (a - b)", "reversed diffrence (b - a)", "xor"], [
|
||||
PolyBool.union, PolyBool.intersect, PolyBool.difference, PolyBool.differenceRev, PolyBool.xor
|
||||
], "What would you like todo with the object");
|
||||
|
||||
const elements = ea.getViewSelectedElements();
|
||||
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;
|
||||
}
|
||||
5
ea-scripts/Boolean Operations.svg
Normal file
|
After Width: | Height: | Size: 17 KiB |
@@ -3,13 +3,60 @@
|
||||
|
||||
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("1.9.19")) {
|
||||
new Notice("This script requires a newer version of Excalidraw. Please install the latest version.");
|
||||
return;
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Utility variables and functions
|
||||
// -------------------------------
|
||||
const excalidrawTemplate = app.metadataCache.getFirstLinkpathDest(ea.plugin.settings.templateFilePath,"");
|
||||
if(typeof window.ExcalidrawDeconstructElements === "undefined") {
|
||||
window.ExcalidrawDeconstructElements = {
|
||||
openDeconstructedImage: true,
|
||||
templatePath: excalidrawTemplate?.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);
|
||||
}
|
||||
|
||||
const templates = settings["Templates"]
|
||||
.value
|
||||
.split(",")
|
||||
.map(p=>app.metadataCache.getFirstLinkpathDest(p.trim(),""))
|
||||
.concat(excalidrawTemplate)
|
||||
.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 +67,114 @@ 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 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;
|
||||
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("deconstructed"),
|
||||
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,"")
|
||||
const file = app.metadataCache.getFirstLinkpathDest(newPath,"");
|
||||
ea.deleteViewElements(els);
|
||||
ea.clear();
|
||||
await ea.addImage(bb.topX,bb.topY,file,false);
|
||||
await ea.addImage(bb.topX,bb.topY,file,false, shouldAnchor);
|
||||
await ea.addElementsToView(false, true, true);
|
||||
ea.getExcalidrawAPI().history.clear(); //to avoid undo/redo messing up the decomposition
|
||||
},1000);
|
||||
|
||||
if(!window.ExcalidrawDeconstructElements.openDeconstructedImage) {
|
||||
new Notice("Deconstruction ready");
|
||||
}
|
||||
@@ -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.||[@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.||[@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.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Split Ellipse](Boolean%20Operations.md)|With This Script it is possible to make boolean Operations on Shapes.||[@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.||[@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.||[@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||[@zsviczian](https://github.com/zsviczian)|
|
||||
@@ -71,8 +72,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.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Split text by lines](Split%20text%20by%20lines.md)|Split lines of text into separate text elements for easier reorganization||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Set Text Alignment](Set%20Text%20Alignment.md)|Sets text alignment of text block (cetner, right, left). Useful if you want to set a keyboard shortcut for selecting text alignment.||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Split Ellipse](Split%20Ellipse.md)|This script splits an ellipse at any point where a line intersects it.||[@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).||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[Toggle Fullscreen on Mobile](Toggle%20Fullscreen%20on%20Mobile.md)|Hides Obsidian workspace leaf padding and header (based on option in settings, default is "hide header" = false) which will take Excalidraw to full screen. ⚠ Note that if the header is not visible, it will be very difficult to invoke the command palette to end full screen. Only hide the header if you have a keyboard or you've practiced opening command palette!||[@zsviczian](https://github.com/zsviczian)|
|
||||
|[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.||[@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)|
|
||||
|
||||
@@ -8,18 +8,44 @@ https://zsviczian.github.io/obsidian-excalidraw-plugin/ExcalidrawScriptsEngine.h
|
||||
|
||||
```javascript
|
||||
*/
|
||||
if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.8.11")) {
|
||||
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
|
||||
});
|
||||
208
ea-scripts/Split Ellipse.md
Normal 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.
|
||||

|
||||

|
||||
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));
|
||||
}
|
||||
10
ea-scripts/Split Ellipse.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
70
ea-scripts/Text Aura.md
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||

|
||||
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
@@ -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
@@ -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
|
||||
});
|
||||
4
ea-scripts/Toggle Grid.svg
Normal 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 |
@@ -25,29 +25,16 @@ I would love to include your contribution in the script library. If you have a s
|
||||
---
|
||||
|
||||
# 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/Ellipse%20Selected%20Elements.svg"/></div>|[[#Ellipse Selected Elements]]|
|
||||
|<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/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]]|
|
||||
@@ -57,39 +44,105 @@ 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/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/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/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/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/Auto%20Draw%20for%20Pen.svg"/></div>|[[#Auto Draw for Pen]]|
|
||||
|<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/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/Hardware%20Eraser%20Support.svg"/></div>|[[#Hardware Eraser Support]]|
|
||||
|<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/Select%20Elements%20of%20Type.svg"/></div>|[[#Select Elements of Type]]|
|
||||
|<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/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/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]]|
|
||||
|
||||
---
|
||||
|
||||
# Description and Installation
|
||||
|
||||
## Add Connector Point
|
||||
```excalidraw-script-install
|
||||
@@ -127,6 +180,13 @@ 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/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 elemtns 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 diffrence operation, so a tranparent 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
|
||||
@@ -197,7 +257,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
|
||||
@@ -433,6 +493,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/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
|
||||
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Split%20text%20by%20lines.md
|
||||
@@ -445,6 +511,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
|
||||
|
||||
BIN
images/scripts-boolean-operations-element-ranking.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
images/scripts-boolean-operations-showcase.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
images/scripts-splitEllipse-demo1.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
images/scripts-splitEllipse-demo2.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
images/scripts-text-aura.jpg
Normal file
|
After Width: | Height: | Size: 118 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.9.6.1-beta",
|
||||
"version": "1.9.19-mermaid",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.9.14",
|
||||
"version": "1.9.19",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
16
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsidian-excalidraw-plugin",
|
||||
"version": "1.9.14",
|
||||
"version": "1.9.15",
|
||||
"description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
@@ -11,19 +11,21 @@
|
||||
"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",
|
||||
"code:fix": "eslint --max-warnings=0 --ext .ts,.tsx ./src --fix"
|
||||
"code:fix": "eslint --max-warnings=0 --ext .ts,.tsx ./src --fix",
|
||||
"madge": "madge --circular ."
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zsviczian/excalidraw": "0.15.2-obsidian-11",
|
||||
"@zsviczian/excalidraw": "0.15.3-obsidian-1",
|
||||
"chroma-js": "^2.4.2",
|
||||
"clsx": "^2.0.0",
|
||||
"colormaster": "^1.2.1",
|
||||
"gl-matrix": "^3.4.3",
|
||||
"lz-string": "^1.5.0",
|
||||
"monkey-around": "^2.3.0",
|
||||
"polybooljs": "^1.2.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"roughjs": "^4.5.2",
|
||||
@@ -34,18 +36,18 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.9",
|
||||
"@babel/preset-env": "^7.22.9",
|
||||
"@babel/preset-env": "^7.22.10",
|
||||
"@babel/preset-react": "^7.22.5",
|
||||
"@excalidraw/eslint-config": "^1.0.3",
|
||||
"@excalidraw/prettier-config": "^1.0.2",
|
||||
"@rollup/plugin-babel": "^6.0.3",
|
||||
"@rollup/plugin-commonjs": "^24.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.1.0",
|
||||
"@rollup/plugin-node-resolve": "^15.2.1",
|
||||
"@rollup/plugin-replace": "^5.0.2",
|
||||
"@rollup/plugin-typescript": "^11.1.2",
|
||||
"@types/chroma-js": "^2.4.0",
|
||||
"@types/js-beautify": "^1.14.0",
|
||||
"@types/node": "^20.4.8",
|
||||
"@types/node": "^20.5.6",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@zerollup/ts-transform-paths": "^1.7.18",
|
||||
"cross-env": "^7.0.3",
|
||||
@@ -61,7 +63,7 @@
|
||||
"rollup-plugin-web-worker-loader": "^1.6.1",
|
||||
"tslib": "^2.6.1",
|
||||
"ttypescript": "^1.5.15",
|
||||
"typescript": "^4.9.5"
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"resolutions": {
|
||||
"@typescript-eslint/typescript-estree": "5.3.0"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import { env } from "process";
|
||||
@@ -6,7 +5,6 @@ import babel from '@rollup/plugin-babel';
|
||||
import replace from "@rollup/plugin-replace";
|
||||
import { terser } from "rollup-plugin-terser";
|
||||
import copy from "rollup-plugin-copy";
|
||||
import ttypescript from "ttypescript";
|
||||
import typescript2 from "rollup-plugin-typescript2";
|
||||
import webWorker from "rollup-plugin-web-worker-loader";
|
||||
import fs from'fs';
|
||||
@@ -60,16 +58,24 @@ const BUILD_CONFIG = {
|
||||
exports: 'default',
|
||||
},
|
||||
plugins: [
|
||||
typescript2({
|
||||
tsconfig: isProd ? "tsconfig.json" : "tsconfig.dev.json",
|
||||
inlineSources: !isProd
|
||||
}),
|
||||
replace({
|
||||
preventAssignment: true,
|
||||
"process.env.NODE_ENV": JSON.stringify(env.NODE_ENV),
|
||||
}),
|
||||
babel({
|
||||
presets: [['@babel/preset-env', {
|
||||
targets: {
|
||||
esmodules: true,
|
||||
},
|
||||
}]],
|
||||
exclude: "node_modules/**"
|
||||
}),
|
||||
commonjs(),
|
||||
nodeResolve({ browser: true, preferBuiltins: false }),
|
||||
typescript({inlineSources: !isProd}),
|
||||
...isProd
|
||||
? [
|
||||
terser({toplevel: false, compress: {passes: 2}}),
|
||||
@@ -98,7 +104,7 @@ const LIB_CONFIG = {
|
||||
name: "Excalidraw (Library)",
|
||||
},
|
||||
plugins: getRollupPlugins(
|
||||
{ tsconfig: "tsconfig-lib.json", typescript: ttypescript },
|
||||
{ tsconfig: "tsconfig-lib.json"},
|
||||
copy({ targets: [{ src: "src/*.d.ts", dest: "lib/typings" }] })
|
||||
),
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
nanoid,
|
||||
THEME_FILTER,
|
||||
VIRGIL_FONT,
|
||||
} from "./Constants";
|
||||
} from "./constants";
|
||||
import { createSVG } from "./ExcalidrawAutomate";
|
||||
import { ExcalidrawData, getTransclusion } from "./ExcalidrawData";
|
||||
import { ExportSettings } from "./ExcalidrawView";
|
||||
@@ -102,9 +102,9 @@ const replaceSVGColors = (svg: SVGSVGElement | string, colorMap: ColorMap | null
|
||||
if(typeof svg === 'string') {
|
||||
// Replace colors in the SVG string
|
||||
for (const [oldColor, newColor] of Object.entries(colorMap)) {
|
||||
const fillRegex = new RegExp(`fill="${oldColor}"`, 'g');
|
||||
const fillRegex = new RegExp(`fill="${oldColor}"`, 'gi');
|
||||
svg = svg.replaceAll(fillRegex, `fill="${newColor}"`);
|
||||
const strokeRegex = new RegExp(`stroke="${oldColor}"`, 'g');
|
||||
const strokeRegex = new RegExp(`stroke="${oldColor}"`, 'gi');
|
||||
svg = svg.replaceAll(strokeRegex, `stroke="${newColor}"`);
|
||||
}
|
||||
return svg;
|
||||
@@ -113,8 +113,8 @@ const replaceSVGColors = (svg: SVGSVGElement | string, colorMap: ColorMap | null
|
||||
// Modify the fill and stroke attributes of child nodes
|
||||
const childNodes = (node: ChildNode) => {
|
||||
if (node instanceof SVGElement) {
|
||||
const oldFill = node.getAttribute('fill');
|
||||
const oldStroke = node.getAttribute('stroke');
|
||||
const oldFill = node.getAttribute('fill')?.toLocaleLowerCase();
|
||||
const oldStroke = node.getAttribute('stroke')?.toLocaleLowerCase();
|
||||
|
||||
if (oldFill && colorMap[oldFill]) {
|
||||
node.setAttribute('fill', colorMap[oldFill]);
|
||||
@@ -158,7 +158,7 @@ export class EmbeddedFile {
|
||||
this.resetImage(hostPath, imgPath);
|
||||
if(this.file && (this.plugin.ea.isExcalidrawFile(this.file) || this.file.extension.toLowerCase() === "svg")) {
|
||||
try {
|
||||
this.colorMap = colorMapJSON ? JSON.parse(colorMapJSON) : null;
|
||||
this.colorMap = colorMapJSON ? JSON.parse(colorMapJSON.toLocaleLowerCase()) : null;
|
||||
} catch (error) {
|
||||
this.colorMap = null;
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ import {
|
||||
restore,
|
||||
REG_LINKINDEX_INVALIDCHARS,
|
||||
THEME_FILTER,
|
||||
} from "src/Constants";
|
||||
mermaidToExcalidraw,
|
||||
} from "src/constants";
|
||||
import { getDrawingFilename, getNewUniqueFilepath, } from "src/utils/FileUtils";
|
||||
import {
|
||||
//debug,
|
||||
@@ -73,10 +74,11 @@ import RYBPlugin from "colormaster/plugins/ryb";
|
||||
import CMYKPlugin from "colormaster/plugins/cmyk";
|
||||
import { TInput } from "colormaster/types";
|
||||
import {ConversionResult, svgToExcalidraw} from "src/svgToExcalidraw/parser"
|
||||
import { ROUNDNESS } from "src/Constants";
|
||||
import { ROUNDNESS } from "src/constants";
|
||||
import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
|
||||
import { emulateKeysForLinkClick, KeyEvent, PaneTarget } from "src/utils/ModifierkeyHelper";
|
||||
import { Mutable } from "@zsviczian/excalidraw/types/utility-types";
|
||||
import PolyBool from "polybooljs";
|
||||
|
||||
extendPlugins([
|
||||
HarmonyPlugin,
|
||||
@@ -430,6 +432,7 @@ export class ExcalidrawAutomate {
|
||||
foldername?: string;
|
||||
templatePath?: string;
|
||||
onNewPane?: boolean;
|
||||
silent?: boolean;
|
||||
frontmatterKeys?: {
|
||||
"excalidraw-plugin"?: "raw" | "parsed";
|
||||
"excalidraw-link-prefix"?: string;
|
||||
@@ -446,6 +449,7 @@ export class ExcalidrawAutomate {
|
||||
};
|
||||
plaintext?: string; //text to insert above the `# Text Elements` section
|
||||
}): Promise<string> {
|
||||
|
||||
const template = params?.templatePath
|
||||
? await getTemplate(
|
||||
this.plugin,
|
||||
@@ -559,17 +563,25 @@ export class ExcalidrawAutomate {
|
||||
return outString;
|
||||
}
|
||||
|
||||
return this.plugin.createAndOpenDrawing(
|
||||
params?.filename
|
||||
? params.filename + (params.filename.endsWith(".md") ? "": ".excalidraw.md")
|
||||
: getDrawingFilename(this.plugin.settings),
|
||||
(params?.onNewPane ? params.onNewPane : false)?"new-pane":"active-pane",
|
||||
params?.foldername ? params.foldername : this.plugin.settings.folder,
|
||||
this.plugin.settings.compatibilityMode
|
||||
? JSON.stringify(scene, null, "\t")
|
||||
: frontmatter + generateMD() +
|
||||
getMarkdownDrawingSection(JSON.stringify(scene, null, "\t"),this.plugin.settings.compress)
|
||||
);
|
||||
const filename = params?.filename
|
||||
? params.filename + (params.filename.endsWith(".md") ? "": ".excalidraw.md")
|
||||
: getDrawingFilename(this.plugin.settings);
|
||||
const foldername = params?.foldername ? params.foldername : this.plugin.settings.folder;
|
||||
const initData = this.plugin.settings.compatibilityMode
|
||||
? JSON.stringify(scene, null, "\t")
|
||||
: frontmatter + generateMD() +
|
||||
getMarkdownDrawingSection(JSON.stringify(scene, null, "\t"),this.plugin.settings.compress)
|
||||
|
||||
if(params.silent) {
|
||||
return (await this.plugin.createDrawing(filename,foldername,initData)).path;
|
||||
} else {
|
||||
return this.plugin.createAndOpenDrawing(
|
||||
filename,
|
||||
(params?.onNewPane ? params.onNewPane : false)?"new-pane":"active-pane",
|
||||
foldername,
|
||||
initData
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -901,7 +913,6 @@ export class ExcalidrawAutomate {
|
||||
[this.getElement(id)],
|
||||
{ x: topX, y: topY },
|
||||
false,
|
||||
this.getExcalidrawAPI(),
|
||||
)[0];
|
||||
return id;
|
||||
};
|
||||
@@ -1129,6 +1140,41 @@ export class ExcalidrawAutomate {
|
||||
return id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a mermaid diagram to ExcalidrawAutomate elements
|
||||
* @param diagram
|
||||
* @returns the ids of the elements that were created
|
||||
*/
|
||||
async addMermaid(
|
||||
diagram: string,
|
||||
): Promise<string[]> {
|
||||
const result = await mermaidToExcalidraw(diagram, {fontSize: this.style.fontSize});
|
||||
const ids:string[] = [];
|
||||
if(!result) return ids;
|
||||
|
||||
if(result?.elements) {
|
||||
result.elements.forEach(el=>{
|
||||
ids.push(el.id);
|
||||
this.elementsDict[el.id] = el;
|
||||
})
|
||||
}
|
||||
|
||||
if(result?.files) {
|
||||
for (const key in result.files) {
|
||||
this.imagesDict[key as FileId] = {
|
||||
...result.files[key],
|
||||
created: Date.now(),
|
||||
isHyperlink: false,
|
||||
hyperlink: null,
|
||||
file: null,
|
||||
hasSVGwithBitmap: false,
|
||||
latex: null,
|
||||
}
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
@@ -2262,6 +2308,16 @@ export class ExcalidrawAutomate {
|
||||
return CM(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the class PolyBool from https://github.com/velipso/polybooljs
|
||||
* @returns
|
||||
*/
|
||||
getPolyool() {
|
||||
const defaultEpsilon = 0.0000000001;
|
||||
PolyBool.epsilon(defaultEpsilon);
|
||||
return PolyBool;
|
||||
}
|
||||
|
||||
importSVG(svgString:string):boolean {
|
||||
const res:ConversionResult = svgToExcalidraw(svgString);
|
||||
if(res.hasErrors) {
|
||||
@@ -2685,7 +2741,6 @@ export function repositionElementsToCursor(
|
||||
elements: ExcalidrawElement[],
|
||||
newPosition: { x: number; y: number },
|
||||
center: boolean = false,
|
||||
api: ExcalidrawImperativeAPI,
|
||||
): ExcalidrawElement[] {
|
||||
const [x1, y1, x2, y2] = estimateBounds(elements);
|
||||
let [offsetX, offsetY] = [0, 0];
|
||||
|
||||
@@ -24,10 +24,10 @@ import {
|
||||
getFontString,
|
||||
wrapText,
|
||||
ERROR_IFRAME_CONVERSION_CANCELED,
|
||||
} from "./Constants";
|
||||
JSON_parse,
|
||||
} from "./constants";
|
||||
import { _measureText } from "./ExcalidrawAutomate";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
import { JSON_parse } from "./Constants";
|
||||
import { TextMode } from "./ExcalidrawView";
|
||||
import {
|
||||
addAppendUpdateCustomData,
|
||||
|
||||
8
src/ExcalidrawLib.d.ts
vendored
@@ -125,4 +125,12 @@ declare namespace ExcalidrawLib {
|
||||
): TElement;
|
||||
|
||||
function getEmbedLink (link: string | null | undefined): EmbeddedLink;
|
||||
|
||||
function mermaidToExcalidraw(
|
||||
mermaidDefinition: string,
|
||||
opts: {fontSize: number},
|
||||
): Promise<{
|
||||
elements: ExcalidrawElement[],
|
||||
files:any
|
||||
} | undefined>;
|
||||
}
|
||||
@@ -48,7 +48,8 @@ import {
|
||||
viewportCoordsToSceneCoords,
|
||||
ERROR_IFRAME_CONVERSION_CANCELED,
|
||||
restore,
|
||||
} from "./Constants";
|
||||
obsidianToExcalidrawMap,
|
||||
} from "./constants";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
import {
|
||||
repositionElementsToCursor,
|
||||
@@ -96,7 +97,7 @@ import {
|
||||
isContainer,
|
||||
fragWithHTML,
|
||||
} from "./utils/Utils";
|
||||
import { getLeaf, getParentOfClass } from "./utils/ObsidianUtils";
|
||||
import { getLeaf, getParentOfClass, obsidianPDFQuoteWithRef } from "./utils/ObsidianUtils";
|
||||
import { splitFolderAndFilename } from "./utils/FileUtils";
|
||||
import { ConfirmationPrompt, GenericInputPrompt, NewFileActions, Prompt } from "./dialogs/Prompt";
|
||||
import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
|
||||
@@ -114,7 +115,7 @@ import { ScriptEngine } from "./Scripts";
|
||||
import { getTextElementAtPointer, getImageElementAtPointer, getElementWithLinkAtPointer } from "./utils/GetElementAtPointer";
|
||||
import { ICONS, LogoWrapper, saveIcon } from "./menu/ActionIcons";
|
||||
import { ExportDialog } from "./dialogs/ExportDialog";
|
||||
import { getEA } from "src";
|
||||
import { getEA } from "src"
|
||||
import { anyModifierKeysPressed, emulateCTRLClickForLinks, emulateKeysForLinkClick, externalDragModifierType, internalDragModifierType, isALT, isCTRL, isMETA, isSHIFT, linkClickModifierType, mdPropModifier, ModifierKeys } from "./utils/ModifierkeyHelper";
|
||||
import { setDynamicStyle } from "./utils/DynamicStyling";
|
||||
import { InsertPDFModal } from "./dialogs/InsertPDFModal";
|
||||
@@ -125,6 +126,7 @@ import { CanvasNodeFactory, ObsidianCanvasNode } from "./utils/CanvasNodeFactory
|
||||
import { EmbeddableMenu } from "./menu/EmbeddableActionsMenu";
|
||||
import { useDefaultExcalidrawFrame } from "./utils/CustomEmbeddableUtils";
|
||||
import { UniversalInsertFileModal } from "./dialogs/UniversalInsertFileModal";
|
||||
import { moment } from "obsidian";
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
|
||||
@@ -1782,10 +1784,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
});
|
||||
}
|
||||
|
||||
private getGridColor(bgColor: string):string {
|
||||
private getGridColor(bgColor: string, st: AppState):{Bold: string, Regular: string, MajorGridFrequency: number} {
|
||||
const cm = this.plugin.ea.getCM(bgColor);
|
||||
cm.isDark() ? cm.lighterBy(5) : cm.darkerBy(5);
|
||||
return cm.stringHEX();
|
||||
const isDark = cm.isDark();
|
||||
const Regular = (isDark ? cm.lighterBy(7) : cm.darkerBy(7)).stringHEX();
|
||||
const Bold = (isDark ? cm.lighterBy(14) : cm.darkerBy(14)).stringHEX();
|
||||
return {Bold, Regular, MajorGridFrequency:st.gridColor.MajorGridFrequency};
|
||||
}
|
||||
|
||||
public activeLoader: EmbeddedFilesLoader = null;
|
||||
@@ -2669,7 +2673,6 @@ export default class ExcalidrawView extends TextFileView {
|
||||
newElements,
|
||||
this.currentPosition,
|
||||
true,
|
||||
api,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2787,6 +2790,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
zoom: st.zoom,
|
||||
currentItemRoundness: st.currentItemRoundness,
|
||||
gridSize: st.gridSize,
|
||||
gridColor: st.gridColor,
|
||||
colorPalette: st.colorPalette,
|
||||
currentStrokeOptions: st.currentStrokeOptions,
|
||||
previousGridSize: st.previousGridSize,
|
||||
@@ -3178,10 +3182,11 @@ export default class ExcalidrawView extends TextFileView {
|
||||
},
|
||||
libraryReturnUrl: "app://obsidian.md",
|
||||
autoFocus: true,
|
||||
langCode: obsidianToExcalidrawMap[moment.locale()]??"en-EN",
|
||||
onChange: (et: ExcalidrawElement[], st: AppState) => {
|
||||
const canvasColorChangeHook = () => {
|
||||
const canvasColor = st.viewBackgroundColor === "transparent" ? "white" : st.viewBackgroundColor;
|
||||
setTimeout(()=>this.updateScene({appState:{gridColor: this.getGridColor(canvasColor)}}));
|
||||
setTimeout(()=>this.updateScene({appState:{gridColor: this.getGridColor(canvasColor, st)}}));
|
||||
setDynamicStyle(this.plugin.ea,this,canvasColor,this.plugin.settings.dynamicStyling);
|
||||
if(this.plugin.ea.onCanvasColorChangeHook) {
|
||||
try {
|
||||
@@ -3270,11 +3275,6 @@ export default class ExcalidrawView extends TextFileView {
|
||||
data: ClipboardData,
|
||||
event: ClipboardEvent | null
|
||||
) => {
|
||||
//, event: ClipboardEvent | null
|
||||
/*if(data && data.text && hyperlinkIsYouTubeLink(data.text)) {
|
||||
this.addYouTubeThumbnail(data.text);
|
||||
return false;
|
||||
}*/
|
||||
const ea = this.getHookServer();
|
||||
if(data && ea.onPasteHook) {
|
||||
const res = ea.onPasteHook({
|
||||
@@ -3291,6 +3291,34 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.addImageWithURL(data.text);
|
||||
return false;
|
||||
}
|
||||
if(data && data.text && !this.modifierKeyDown.shiftKey) {
|
||||
const quoteWithRef = obsidianPDFQuoteWithRef(data.text);
|
||||
if(quoteWithRef) {
|
||||
const ea = getEA(this) as ExcalidrawAutomate;
|
||||
const api = this.excalidrawAPI as ExcalidrawImperativeAPI;
|
||||
const st = api.getAppState();
|
||||
const strokeC = st.currentItemStrokeColor;
|
||||
const viewC = st.viewBackgroundColor;
|
||||
ea.style.strokeColor = strokeC === "transparent"
|
||||
? ea.getCM(viewC === "transparent" ? "white" : viewC)
|
||||
.invert()
|
||||
.stringHEX({alpha: false})
|
||||
: strokeC;
|
||||
ea.style.fontFamily = st.currentItemFontFamily;
|
||||
ea.style.fontSize = st.currentItemFontSize;
|
||||
const textDims = ea.measureText(quoteWithRef.quote);
|
||||
const textWidth = textDims.width + 2*30; //default padding
|
||||
const id = ea.addText(this.currentPosition.x, this.currentPosition.y, quoteWithRef.quote, {
|
||||
box: true,
|
||||
boxStrokeColor: "transparent",
|
||||
width: Math.min(500,textWidth),
|
||||
height: textDims.height + 2*30,
|
||||
})
|
||||
ea.elementsDict[id].link = `[[${quoteWithRef.link}]]`;
|
||||
ea.addElementsToView(false,false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (data.elements) {
|
||||
const self = this;
|
||||
setTimeout(() => self.save(false), 300);
|
||||
@@ -4131,12 +4159,8 @@ export default class ExcalidrawView extends TextFileView {
|
||||
|
||||
return React.createElement(React.Fragment, null, excalidrawDiv);
|
||||
});
|
||||
//REACT 18
|
||||
const root = ReactDOM.createRoot(this.contentEl);
|
||||
root.render(reactElement);
|
||||
/*REACT 17
|
||||
ReactDOM.render(reactElement, this.contentEl, () => {});
|
||||
*/
|
||||
}
|
||||
|
||||
private updateContainerSize(containerId?: string, delay: boolean = false) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import ExcalidrawPlugin from "./main";
|
||||
import { FileData, MimeType } from "./EmbeddedFileLoader";
|
||||
import { FileId } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { errorlog, getImageSize, log, sleep, svgToBase64 } from "./utils/Utils";
|
||||
import { fileid } from "./Constants";
|
||||
import { fileid } from "./constants";
|
||||
import html2canvas from "html2canvas";
|
||||
import { Notice } from "obsidian";
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
TFile,
|
||||
Vault,
|
||||
} from "obsidian";
|
||||
import { RERENDER_EVENT } from "./Constants";
|
||||
import { RERENDER_EVENT } from "./constants";
|
||||
import { EmbeddedFilesLoader } from "./EmbeddedFileLoader";
|
||||
import { createPNG, createSVG } from "./ExcalidrawAutomate";
|
||||
import { ExportSettings } from "./ExcalidrawView";
|
||||
@@ -316,6 +316,9 @@ const createImgElement = async (
|
||||
onCanvas: boolean = false,
|
||||
) :Promise<HTMLElement> => {
|
||||
const imgOrDiv = await getIMG(attr,onCanvas);
|
||||
if(!imgOrDiv) {
|
||||
return null;
|
||||
}
|
||||
imgOrDiv.setAttribute("fileSource", attr.fname);
|
||||
if (attr.fwidth) {
|
||||
imgOrDiv.setAttribute("w", attr.fwidth);
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
TFile,
|
||||
WorkspaceLeaf,
|
||||
} from "obsidian";
|
||||
import { PLUGIN_ID, VIEW_TYPE_EXCALIDRAW } from "./Constants";
|
||||
import { PLUGIN_ID, VIEW_TYPE_EXCALIDRAW } from "./constants";
|
||||
import ExcalidrawView from "./ExcalidrawView";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
import { ButtonDefinition, GenericInputPrompt, GenericSuggester } from "./dialogs/Prompt";
|
||||
@@ -261,10 +261,10 @@ export class ScriptEngine {
|
||||
/*} catch (e) {
|
||||
new Notice(t("SCRIPT_EXECUTION_ERROR"), 4000);
|
||||
errorlog({ script: this.plugin.ea.activeScript, error: e });
|
||||
}*/
|
||||
this.plugin.ea.activeScript = null;
|
||||
return result;
|
||||
}
|
||||
}*/
|
||||
this.plugin.ea.activeScript = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
private updateToolPannels() {
|
||||
const leaves =
|
||||
|
||||
@@ -8,6 +8,60 @@ export const ERROR_IFRAME_CONVERSION_CANCELED = "iframe conversion canceled";
|
||||
|
||||
declare const excalidrawLib: typeof ExcalidrawLib;
|
||||
|
||||
export const obsidianToExcalidrawMap: { [key: string]: string } = {
|
||||
'en': 'en-US',
|
||||
'af': 'af-ZA', // Assuming South Africa for Afrikaans
|
||||
'am': 'am-ET', // Assuming Ethiopia for Amharic
|
||||
'ar': 'ar-SA',
|
||||
'eu': 'eu-ES',
|
||||
'be': 'be-BY', // Assuming Belarus for Belarusian
|
||||
'bg': 'bg-BG',
|
||||
'bn': 'bn-BD', // Assuming Bangladesh for Bengali
|
||||
'ca': 'ca-ES',
|
||||
'cs': 'cs-CZ',
|
||||
'da': 'da-DK', // Assuming Denmark for Danish
|
||||
'de': 'de-DE',
|
||||
'el': 'el-GR',
|
||||
'eo': 'eo-EO', // Esperanto doesn't have a country
|
||||
'es': 'es-ES',
|
||||
'fa': 'fa-IR',
|
||||
'fi-fi': 'fi-FI',
|
||||
'fr': 'fr-FR',
|
||||
'gl': 'gl-ES',
|
||||
'he': 'he-IL',
|
||||
'hi': 'hi-IN',
|
||||
'hu': 'hu-HU',
|
||||
'id': 'id-ID',
|
||||
'it': 'it-IT',
|
||||
'ja': 'ja-JP',
|
||||
'ko': 'ko-KR',
|
||||
'lv': 'lv-LV',
|
||||
'ml': 'ml-IN', // Assuming India for Malayalam
|
||||
'ms': 'ms-MY', // Assuming Malaysia for Malay
|
||||
'nl': 'nl-NL',
|
||||
'no': 'nb-NO', // Using Norwegian Bokmål for Norwegian
|
||||
'oc': 'oc-FR', // Assuming France for Occitan
|
||||
'pl': 'pl-PL',
|
||||
'pt': 'pt-PT',
|
||||
'pt-BR': 'pt-BR',
|
||||
'ro': 'ro-RO',
|
||||
'ru': 'ru-RU',
|
||||
'sr': 'sr-RS', // Assuming Serbia for Serbian
|
||||
'se': 'sv-SE', // Assuming Swedish for 'se'
|
||||
'sk': 'sk-SK',
|
||||
'sq': 'sq-AL', // Assuming Albania for Albanian
|
||||
'ta': 'ta-IN', // Assuming India for Tamil
|
||||
'te': 'te-IN', // Assuming India for Telugu
|
||||
'th': 'th-TH',
|
||||
'tr': 'tr-TR',
|
||||
'uk': 'uk-UA',
|
||||
'ur': 'ur-PK', // Assuming Pakistan for Urdu
|
||||
'vi': 'vi-VN',
|
||||
'zh': 'zh-CN',
|
||||
'zh-TW': 'zh-TW',
|
||||
};
|
||||
|
||||
|
||||
export const {
|
||||
sceneCoordsToViewportCoords,
|
||||
viewportCoordsToSceneCoords,
|
||||
@@ -24,6 +78,7 @@ export const {
|
||||
exportToBlob,
|
||||
mutateElement,
|
||||
restore,
|
||||
mermaidToExcalidraw,
|
||||
} = excalidrawLib;
|
||||
|
||||
export function JSON_parse(x: string): any {
|
||||
|
||||
@@ -3,7 +3,7 @@ import ExcalidrawView from "./ExcalidrawView";
|
||||
import { Notice, WorkspaceLeaf, WorkspaceSplit } from "obsidian";
|
||||
import * as React from "react";
|
||||
import { ConstructableWorkspaceSplit, getContainerForDocument, isObsidianThemeDark } from "./utils/ObsidianUtils";
|
||||
import { DEVICE, EXTENDED_EVENT_TYPES, KEYBOARD_EVENT_TYPES } from "./Constants";
|
||||
import { DEVICE, EXTENDED_EVENT_TYPES, KEYBOARD_EVENT_TYPES } from "./constants";
|
||||
import { ExcalidrawImperativeAPI, UIAppState } from "@zsviczian/excalidraw/types/types";
|
||||
import { ObsidianCanvasNode } from "./utils/CanvasNodeFactory";
|
||||
import { processLinkText, patchMobileView } from "./utils/CustomEmbeddableUtils";
|
||||
@@ -141,6 +141,8 @@ function RenderObsidianView(
|
||||
containerRef.current.removeChild(containerRef.current.lastChild);
|
||||
}
|
||||
|
||||
containerRef.current.parentElement.style.padding = "";
|
||||
|
||||
const doc = view.ownerDocument;
|
||||
const rootSplit:WorkspaceSplit = new (WorkspaceSplit as ConstructableWorkspaceSplit)(app.workspace, "vertical");
|
||||
rootSplit.getRoot = () => app.workspace[doc === document ? 'rootSplit' : 'floatingSplit'];
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/types";
|
||||
import { Modal, Setting, SliderComponent, TFile } from "obsidian";
|
||||
import { Modal, Setting, TFile } from "obsidian";
|
||||
import { getEA } from "src";
|
||||
import { DEVICE } from "src/Constants";
|
||||
import { DEVICE } from "src/constants";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import ExcalidrawView from "src/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { App, FuzzySuggestModal, TFile } from "obsidian";
|
||||
import { REG_LINKINDEX_INVALIDCHARS } from "../Constants";
|
||||
import { REG_LINKINDEX_INVALIDCHARS } from "../constants";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import { t } from "../lang/helpers";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { App, FuzzySuggestModal, TFile } from "obsidian";
|
||||
import { isALT, scaleToFullsizeModifier } from "src/utils/ModifierkeyHelper";
|
||||
import { fileURLToPath } from "url";
|
||||
import { DEVICE, IMAGE_TYPES, REG_LINKINDEX_INVALIDCHARS } from "../Constants";
|
||||
import { DEVICE, IMAGE_TYPES, REG_LINKINDEX_INVALIDCHARS } from "../constants";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import { t } from "../lang/helpers";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { App, FuzzySuggestModal, TFile } from "obsidian";
|
||||
import { REG_LINKINDEX_INVALIDCHARS } from "../Constants";
|
||||
import { REG_LINKINDEX_INVALIDCHARS } from "../constants";
|
||||
import { t } from "../lang/helpers";
|
||||
|
||||
export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
|
||||
|
||||
@@ -17,6 +17,75 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
|
||||
|
||||
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://cdn.ko-fi.com/cdn/kofi3.png?v=3" height=45></a></div>
|
||||
`,
|
||||
"1.9.19":`
|
||||
## New
|
||||
- I added new features to the [Deconstruct Selected Elements](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Deconstruct%20selected%20elements%20into%20new%20drawing.md) script
|
||||
- I added a new script: [Text Aura](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Text%20Aura.md)
|
||||
- I updated the [Set Grid](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Set%20Grid.md) script. You can now set the Major/Minor tick frequency. [#1305](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1305)
|
||||
- The re-colorMap is now case-insensitive. The color map is a hidden feature. In Markdown View mode you can add a JSON map after the embedded SVG or Excalidraw image filename with a mapping of current colors to new colors.
|
||||
<img width="100%" src="https://github.com/zsviczian/obsidian-excalidraw-plugin/assets/14358394/1d985a59-a2d2-48a2-9cef-686bfbe9ef02"/>
|
||||
|
||||
## New in ExcalidrawAutomate
|
||||
- I added the ${String.fromCharCode(96)}silent${String.fromCharCode(96)} switch. If this is true, the created file will not be opened.
|
||||
${String.fromCharCode(96,96,96)}typescript
|
||||
async create(params?: {
|
||||
filename?: string;
|
||||
foldername?: string;
|
||||
templatePath?: string;
|
||||
onNewPane?: boolean;
|
||||
silent?: 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; //text to insert above the ${String.fromCharCode(96)}# Text Elements${String.fromCharCode(96)} section
|
||||
}): Promise<string>
|
||||
${String.fromCharCode(96,96,96)}
|
||||
`,
|
||||
"1.9.18":`
|
||||
## New
|
||||
- Excalidraw now syncs with Obsidian's language settings, provided translations are available. [#1297](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1297)
|
||||
|
||||
## Fixed
|
||||
- [#1285](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1285): Solved Obsidian crashes caused by copying images from Excalidraw into markdown notes. Going forward:
|
||||
- Copying an image will paste its embed link,
|
||||
- Copying a text element will paste the text,
|
||||
- For all other elements with links, the link will be pasted.
|
||||
- In all other cases nothing will be pasted.
|
||||
|
||||
- Resolved grid instability ([#1298](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1298)).
|
||||
- Fixed missing ${String.fromCharCode(96)}[[square brackets]]${String.fromCharCode(96)} in PDF section references, making the links functional.
|
||||
- Corrected the behavior of "Open current link in browser" for embedded YouTube and Vimeo frames. Clicking the globe button will now correctly open the links.
|
||||
`,
|
||||
"1.9.17":`
|
||||
## New
|
||||
- Significant performance improvements from Excalidraw.com
|
||||
- When selecting a highlight in the Obsidian PDF editor and selecting "Copy as Quote" in the context menu, then paste this to Excalidraw, the text will arrive as a text element wrapped in a transparent sticky note with the link to the original highlight attached to the sticky note. You can override this behavior by SHIFT+CTRL/CMD pasting
|
||||
|
||||
## Fixed
|
||||
- BUG: Image caching issue. Changes to the drawing do not reflect immediately in the note when re-opening the drawing [#1297](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1279)
|
||||
- Removed underline from links in NativeSVG embed.
|
||||
`,
|
||||
"1.9.16":`
|
||||
I apologize for this extra release. I accidentally built 1.9.15 with an older excalidraw.com package version. Fixes and new features (like the improved grid) are now available again. Otherwise, this is the same as 1.9.15. Sorry for the inconvenience.
|
||||
`,
|
||||
"1.9.15":`
|
||||
## New
|
||||
- There is now a search box in the Excliadraw Script Store. I categorized the scripts and added keywords to help easier navigation.
|
||||
|
||||
## Fixed
|
||||
- The theme of the embedded Markdown document did not always honor plugin settings. With some themes, it worked, with others (including the default Obsidian theme, it didn't).
|
||||
`,
|
||||
"1.9.14":`
|
||||
# Fixed
|
||||
- **Dynamic Styling**: Excalidraw ${String.fromCharCode(96)}Plugin Settings/Display/Dynamic Styling${String.fromCharCode(96)} did not handle theme changes correctly.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { App, FuzzySuggestModal, TFile } from "obsidian";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { EMPTY_MESSAGE } from "../Constants";
|
||||
import { EMPTY_MESSAGE } from "../constants";
|
||||
import { t } from "../lang/helpers";
|
||||
|
||||
export enum openDialogAction {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/types";
|
||||
import { ColorComponent, Modal, Setting, SliderComponent, TextComponent, ToggleComponent } from "obsidian";
|
||||
import { COLOR_NAMES, VIEW_TYPE_EXCALIDRAW } from "src/Constants";
|
||||
import { COLOR_NAMES, VIEW_TYPE_EXCALIDRAW } from "src/constants";
|
||||
import ExcalidrawView from "src/ExcalidrawView";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { setPen } from "src/menu/ObsidianMenu";
|
||||
|
||||
@@ -6,13 +6,74 @@ const URL =
|
||||
"https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/index-new.md";
|
||||
|
||||
export class ScriptInstallPrompt extends Modal {
|
||||
private contentDiv: HTMLDivElement;
|
||||
constructor(private plugin: ExcalidrawPlugin) {
|
||||
super(plugin.app);
|
||||
// this.titleEl.setText(t("INSTAL_MODAL_TITLE"));
|
||||
}
|
||||
|
||||
async onOpen(): Promise<void> {
|
||||
const searchBarWrapper = document.createElement("div");
|
||||
searchBarWrapper.classList.add('search-bar-wrapper');
|
||||
|
||||
|
||||
const searchBar = document.createElement("input");
|
||||
searchBar.type = "text";
|
||||
searchBar.id = "search-bar";
|
||||
searchBar.placeholder = "Search...";
|
||||
searchBar.style.width = "calc(100% - 120px)"; // space for the buttons and hit count
|
||||
|
||||
const nextButton = document.createElement("button");
|
||||
nextButton.textContent = "→";
|
||||
nextButton.onclick = () => this.navigateSearchResults("next");
|
||||
|
||||
const prevButton = document.createElement("button");
|
||||
prevButton.textContent = "←";
|
||||
prevButton.onclick = () => this.navigateSearchResults("previous");
|
||||
|
||||
const hitCount = document.createElement("span");
|
||||
hitCount.id = "hit-count";
|
||||
hitCount.classList.add('hit-count');
|
||||
|
||||
searchBarWrapper.appendChild(prevButton);
|
||||
searchBarWrapper.appendChild(nextButton);
|
||||
searchBarWrapper.appendChild(searchBar);
|
||||
searchBarWrapper.appendChild(hitCount);
|
||||
|
||||
this.contentEl.prepend(searchBarWrapper);
|
||||
|
||||
searchBar.addEventListener("input", (e) => {
|
||||
this.clearHighlights();
|
||||
const searchTerm = (e.target as HTMLInputElement).value;
|
||||
|
||||
if (searchTerm && searchTerm.length > 0) {
|
||||
this.highlightSearchTerm(searchTerm);
|
||||
const totalHits = this.contentDiv.querySelectorAll("mark.search-highlight").length;
|
||||
hitCount.textContent = totalHits > 0 ? `1/${totalHits}` : "";
|
||||
setTimeout(()=>this.navigateSearchResults("next"));
|
||||
} else {
|
||||
hitCount.textContent = "";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
searchBar.addEventListener("keydown", (e) => {
|
||||
// If Ctrl/Cmd + F is pressed, focus on search bar
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === "f") {
|
||||
e.preventDefault();
|
||||
searchBar.focus();
|
||||
}
|
||||
// If Enter is pressed, navigate to next result
|
||||
else if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
this.navigateSearchResults(e.shiftKey ? "previous" : "next");
|
||||
}
|
||||
});
|
||||
|
||||
this.contentEl.classList.add("excalidraw-scriptengine-install");
|
||||
this.contentDiv = document.createElement("div");
|
||||
this.contentEl.appendChild(this.contentDiv);
|
||||
|
||||
this.containerEl.classList.add("excalidraw-scriptengine-install");
|
||||
try {
|
||||
const source = await request({ url: URL });
|
||||
@@ -29,16 +90,16 @@ export class ScriptInstallPrompt extends Modal {
|
||||
}
|
||||
await MarkdownRenderer.renderMarkdown(
|
||||
source,
|
||||
this.contentEl,
|
||||
this.contentDiv,
|
||||
"",
|
||||
this.plugin,
|
||||
);
|
||||
this.contentEl
|
||||
this.contentDiv
|
||||
.querySelectorAll("h1[data-heading],h2[data-heading],h3[data-heading]")
|
||||
.forEach((el) => {
|
||||
el.setAttribute("id", el.getAttribute("data-heading"));
|
||||
});
|
||||
this.contentEl.querySelectorAll("a.internal-link").forEach((el) => {
|
||||
this.contentDiv.querySelectorAll("a.internal-link").forEach((el) => {
|
||||
el.removeAttribute("target");
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -48,6 +109,99 @@ export class ScriptInstallPrompt extends Modal {
|
||||
}
|
||||
}
|
||||
|
||||
highlightSearchTerm(searchTerm: string): void {
|
||||
// Create a walker to traverse text nodes
|
||||
const walker = document.createTreeWalker(
|
||||
this.contentDiv,
|
||||
NodeFilter.SHOW_TEXT,
|
||||
{
|
||||
acceptNode: (node: Text) => {
|
||||
return node.nodeValue!.toLowerCase().includes(searchTerm.toLowerCase()) ?
|
||||
NodeFilter.FILTER_ACCEPT :
|
||||
NodeFilter.FILTER_REJECT;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const nodesToReplace: Text[] = [];
|
||||
while (walker.nextNode()) {
|
||||
nodesToReplace.push(walker.currentNode as Text);
|
||||
}
|
||||
|
||||
nodesToReplace.forEach(node => {
|
||||
const nodeContent = node.nodeValue!;
|
||||
const newNode = document.createDocumentFragment();
|
||||
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
const regex = new RegExp(searchTerm, 'gi');
|
||||
|
||||
// Iterate over all matches in the text node
|
||||
while ((match = regex.exec(nodeContent)) !== null) {
|
||||
const before = document.createTextNode(nodeContent.slice(lastIndex, match.index));
|
||||
const highlighted = document.createElement('mark');
|
||||
highlighted.className = 'search-highlight';
|
||||
highlighted.textContent = match[0];
|
||||
highlighted.classList.add('search-result');
|
||||
|
||||
newNode.appendChild(before);
|
||||
newNode.appendChild(highlighted);
|
||||
|
||||
lastIndex = regex.lastIndex;
|
||||
}
|
||||
newNode.appendChild(document.createTextNode(nodeContent.slice(lastIndex)));
|
||||
node.replaceWith(newNode);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
clearHighlights(): void {
|
||||
this.contentDiv.querySelectorAll("mark.search-highlight").forEach((el) => {
|
||||
el.outerHTML = el.innerHTML;
|
||||
});
|
||||
}
|
||||
|
||||
navigateSearchResults(direction: "next" | "previous"): void {
|
||||
const highlights: HTMLElement[] = Array.from(
|
||||
this.contentDiv.querySelectorAll("mark.search-highlight")
|
||||
);
|
||||
|
||||
if (highlights.length === 0) return;
|
||||
|
||||
const currentActiveIndex = highlights.findIndex((highlight) =>
|
||||
highlight.classList.contains("active-highlight")
|
||||
);
|
||||
|
||||
if (currentActiveIndex !== -1) {
|
||||
highlights[currentActiveIndex].classList.remove("active-highlight");
|
||||
highlights[currentActiveIndex].style.border = "none";
|
||||
}
|
||||
|
||||
let nextActiveIndex = 0;
|
||||
if (direction === "next") {
|
||||
nextActiveIndex =
|
||||
currentActiveIndex === highlights.length - 1
|
||||
? 0
|
||||
: currentActiveIndex + 1;
|
||||
} else if (direction === "previous") {
|
||||
nextActiveIndex =
|
||||
currentActiveIndex === 0
|
||||
? highlights.length - 1
|
||||
: currentActiveIndex - 1;
|
||||
}
|
||||
|
||||
const nextActiveHighlight = highlights[nextActiveIndex];
|
||||
nextActiveHighlight.classList.add("active-highlight");
|
||||
nextActiveHighlight.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "nearest",
|
||||
});
|
||||
|
||||
// Update the hit count
|
||||
const hitCount = document.getElementById("hit-count");
|
||||
hitCount.textContent = `${nextActiveIndex + 1}/${highlights.length}`;
|
||||
}
|
||||
|
||||
onClose(): void {
|
||||
this.contentEl.empty();
|
||||
}
|
||||
|
||||
@@ -170,8 +170,8 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
},
|
||||
{
|
||||
field: "create",
|
||||
code: '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,},}): Promise<string>;',
|
||||
desc: "Create a drawing and save it to filename.\nIf filename is null: default filename as defined in Excalidraw settings.\nIf folder is null: default folder as defined in Excalidraw settings\n",
|
||||
code: 'create(params?: {filename?: string, foldername?: string, templatePath?: string, onNewPane?: boolean, silent?: boolean, frontmatterKeys?: { "excalidraw-plugin"?: "raw" | "parsed", "excalidraw-link-prefix"?: string, "excalidraw-link-brackets"?: boolean, "excalidraw-url-prefix"?: string,},}): Promise<string>;',
|
||||
desc: "Create a drawing and save it to filename.\nIf filename is null: default filename as defined in Excalidraw settings.\nIf folder is null: default folder as defined in Excalidraw settings\nReturns the path to the created file",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,12 +3,11 @@ import ExcalidrawView from "../ExcalidrawView";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { Modal, Setting, TextComponent } from "obsidian";
|
||||
import { FileSuggestionModal } from "./FolderSuggester";
|
||||
import { IMAGE_TYPES, sceneCoordsToViewportCoords, viewportCoordsToSceneCoords } from "src/Constants";
|
||||
import { IMAGE_TYPES, sceneCoordsToViewportCoords, viewportCoordsToSceneCoords, MAX_IMAGE_SIZE } from "src/constants";
|
||||
import { insertEmbeddableToView, insertImageToView } from "src/utils/ExcalidrawViewUtils";
|
||||
import { getEA } from "src";
|
||||
import { InsertPDFModal } from "./InsertPDFModal";
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/types";
|
||||
import { MAX_IMAGE_SIZE } from "src/Constants";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import { cleanSectionHeading } from "src/utils/ObsidianUtils";
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS,
|
||||
FRONTMATTER_KEY_CUSTOM_PREFIX,
|
||||
FRONTMATTER_KEY_CUSTOM_URL_PREFIX,
|
||||
} from "src/Constants";
|
||||
} from "src/constants";
|
||||
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/ModifierkeyHelper";
|
||||
|
||||
// English
|
||||
@@ -103,12 +103,12 @@ export default {
|
||||
//settings.ts
|
||||
RELEASE_NOTES_NAME: "Display Release Notes after update",
|
||||
RELEASE_NOTES_DESC:
|
||||
"<b>Toggle ON:</b> Display release notes each time you update Excalidraw to a newer version.<br>" +
|
||||
"<b>Toggle OFF:</b> Silent mode. You can still read release notes on <a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/releases'>GitHub</a>.",
|
||||
"<b><u>Toggle ON:</u></b> Display release notes each time you update Excalidraw to a newer version.<br>" +
|
||||
"<b><u>Toggle OFF:</u></b> Silent mode. You can still read release notes on <a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/releases'>GitHub</a>.",
|
||||
NEWVERSION_NOTIFICATION_NAME: "Plugin update notification",
|
||||
NEWVERSION_NOTIFICATION_DESC:
|
||||
"<b>Toggle ON:</b> Show a notification when a new version of the plugin is available.<br>" +
|
||||
"<b>Toggle OFF:</b> Silent mode. You need to check for plugin updates in Community Plugins.",
|
||||
"<b><u>Toggle ON:</u></b> Show a notification when a new version of the plugin is available.<br>" +
|
||||
"<b><u>Toggle OFF:</u></b> Silent mode. You need to check for plugin updates in Community Plugins.",
|
||||
|
||||
FOLDER_NAME: "Excalidraw folder",
|
||||
FOLDER_DESC:
|
||||
@@ -118,7 +118,7 @@ export default {
|
||||
FOLDER_EMBED_DESC:
|
||||
"Define which folder to place the newly inserted drawing into " +
|
||||
"when using the command palette action: 'Create a new drawing and embed into active document'.<br>" +
|
||||
"<b>Toggle ON:</b> Use Excalidraw folder<br><b>Toggle OFF:</b> Use the attachments folder defined in Obsidian settings.",
|
||||
"<b><u>Toggle ON:</u></b> Use Excalidraw folder<br><b><u>Toggle OFF:</u></b> Use the attachments folder defined in Obsidian settings.",
|
||||
TEMPLATE_NAME: "Excalidraw template file",
|
||||
TEMPLATE_DESC:
|
||||
"Full filepath to the Excalidraw template. " +
|
||||
@@ -143,7 +143,7 @@ export default {
|
||||
"be saved without compression, so that you can read and edit the JSON string. The drawing will be compressed again " +
|
||||
"once you switch back to Excalidraw view. " +
|
||||
"The setting only has effect 'point forward', meaning, existing drawings will not be affected by the setting " +
|
||||
"until you open them and save them.<br><b>Toggle ON:</b> Compress drawing JSON<br><b>Toggle OFF:</b> Leave drawing JSON uncompressed",
|
||||
"until you open them and save them.<br><b><u>Toggle ON:</u></b> Compress drawing JSON<br><b><u>Toggle OFF:</u></b> Leave drawing JSON uncompressed",
|
||||
AUTOSAVE_INTERVAL_DESKTOP_NAME: "Interval for autosave on Desktop",
|
||||
AUTOSAVE_INTERVAL_DESKTOP_DESC:
|
||||
"The time interval between saves. Autosave will skip if there are no changes in the drawing. " +
|
||||
@@ -168,7 +168,7 @@ FILENAME_HEAD: "Filename",
|
||||
FILENAME_PREFIX_EMBED_DESC:
|
||||
"Should the filename of the newly inserted drawing start with the name of the active markdown note " +
|
||||
"when using the command palette action: <code>Create a new drawing and embed into active document</code>?<br>" +
|
||||
"<b>Toggle ON:</b> Yes, the filename of a new drawing should start with filename of the active document<br><b>Toggle OFF:</b> No, filename of a new drawing should not include the filename of the active document",
|
||||
"<b><u>Toggle ON:</u></b> Yes, the filename of a new drawing should start with filename of the active document<br><b><u>Toggle OFF:</u></b> No, filename of a new drawing should not include the filename of the active document",
|
||||
FILENAME_POSTFIX_NAME:
|
||||
"Custom text after markdown Note's name when embedding",
|
||||
FILENAME_POSTFIX_DESC:
|
||||
@@ -179,7 +179,7 @@ FILENAME_HEAD: "Filename",
|
||||
FILENAME_EXCALIDRAW_EXTENSION_NAME: ".excalidraw.md or .md",
|
||||
FILENAME_EXCALIDRAW_EXTENSION_DESC:
|
||||
"This setting does not apply if you use Excalidraw in compatibility mode, " +
|
||||
"i.e. you are not using Excalidraw markdown files.<br><b>Toggle ON:</b> filename ends with .excalidraw.md<br><b>Toggle OFF:</b> filename ends with .md",
|
||||
"i.e. you are not using Excalidraw markdown files.<br><b><u>Toggle ON:</u></b> filename ends with .excalidraw.md<br><b><u>Toggle OFF:</u></b> filename ends with .md",
|
||||
DISPLAY_HEAD: "Display",
|
||||
DYNAMICSTYLE_NAME: "Dynamic styling",
|
||||
DYNAMICSTYLE_DESC:
|
||||
@@ -187,24 +187,25 @@ FILENAME_HEAD: "Filename",
|
||||
LEFTHANDED_MODE_NAME: "Left-handed mode",
|
||||
LEFTHANDED_MODE_DESC:
|
||||
"Currently only has effect in tray-mode. If turned on, the tray will be on the right side." +
|
||||
"<br><b>Toggle ON:</b> Left-handed mode.<br><b>Toggle OFF:</b> Right-handed moded",
|
||||
IFRAME_MATCH_THEME_NAME: "IFrames (markdown embeds) to match Excalidraw theme",
|
||||
"<br><b><u>Toggle ON:</u></b> Left-handed mode.<br><b><u>Toggle OFF:</u></b> Right-handed moded",
|
||||
IFRAME_MATCH_THEME_NAME: "Markdown embeds to match Excalidraw theme",
|
||||
IFRAME_MATCH_THEME_DESC:
|
||||
"Set this to true if you are for example using Obsidian in dark mode but use excalidraw with a light background. " +
|
||||
"With this setting the embedded Obsidian markdown document will match the Excalidraw theme (i.e. light colors if Excalidraw is in light mode). ",
|
||||
"<b><u>Toggle ON:</u></b> Set this to true if for example you are using Obsidian in dark-mode but use excalidraw with a light background. " +
|
||||
"With this setting the embedded Obsidian markdown document will match the Excalidraw theme (i.e. light colors if Excalidraw is in light mode).<br>" +
|
||||
"<b><u>Toggle OFF:</u></b> Set this to false if you want the embedded Obsidian markdown document to match the Obsidian theme (i.e. dark colors if Obsidian is in dark mode).",
|
||||
MATCH_THEME_NAME: "New drawing to match Obsidian theme",
|
||||
MATCH_THEME_DESC:
|
||||
"If theme is dark, new drawing will be created in dark mode. This does not apply when you use a template for new drawings. " +
|
||||
"Also this will not affect when you open an existing drawing. Those will follow the theme of the template/drawing respectively." +
|
||||
"<br><b>Toggle ON:</b> Follow Obsidian Theme<br><b>Toggle OFF:</b> Follow theme defined in your template",
|
||||
"<br><b><u>Toggle ON:</u></b> Follow Obsidian Theme<br><b><u>Toggle OFF:</u></b> Follow theme defined in your template",
|
||||
MATCH_THEME_ALWAYS_NAME: "Existing drawings to match Obsidian theme",
|
||||
MATCH_THEME_ALWAYS_DESC:
|
||||
"If theme is dark, drawings will be opened in dark mode. If your theme is light, they will be opened in light mode. " +
|
||||
"<br><b>Toggle ON:</b> Match Obsidian theme<br><b>Toggle OFF:</b> Open with the same theme as last saved",
|
||||
"<br><b><u>Toggle ON:</u></b> Match Obsidian theme<br><b><u>Toggle OFF:</u></b> Open with the same theme as last saved",
|
||||
MATCH_THEME_TRIGGER_NAME: "Excalidraw to follow when Obsidian Theme changes",
|
||||
MATCH_THEME_TRIGGER_DESC:
|
||||
"If this option is enabled open Excalidraw pane will switch to light/dark mode when Obsidian theme changes. " +
|
||||
"<br><b>Toggle ON:</b> Follow theme changes<br><b>Toggle OFF:</b> Drawings are not affected by Obsidian theme changes",
|
||||
"<br><b><u>Toggle ON:</u></b> Follow theme changes<br><b><u>Toggle OFF:</u></b> Drawings are not affected by Obsidian theme changes",
|
||||
DEFAULT_OPEN_MODE_NAME: "Default mode when opening Excalidraw",
|
||||
DEFAULT_OPEN_MODE_DESC:
|
||||
"Specifies the mode how Excalidraw opens: Normal, Zen, or View mode. You may also set this behavior on a file level by " +
|
||||
@@ -216,18 +217,18 @@ FILENAME_HEAD: "Filename",
|
||||
DEFAULT_PINCHZOOM_NAME: "Allow pinch zoom in pen mode",
|
||||
DEFAULT_PINCHZOOM_DESC:
|
||||
"Pinch zoom in pen mode when using the freedraw tool is disabled by default to prevent unwanted accidental zooming with your palm.<br>" +
|
||||
"<b>Toggle on: </b>Enable pinch zoom in pen mode<br><b>Toggle off: </b>Disable pinch zoom in pen mode",
|
||||
"<b><u>Toggle ON:</u></b> Enable pinch zoom in pen mode<br><b><u>Toggle OFF:</u></b>Disable pinch zoom in pen mode",
|
||||
|
||||
DEFAULT_WHEELZOOM_NAME: "Mouse wheel to zoom by default",
|
||||
DEFAULT_WHEELZOOM_DESC:
|
||||
`<b>Toggle on: </b>Mouse wheel to zoom; ${labelCTRL()} + mouse wheel to scroll</br><b>Toggle off: </b>${labelCTRL()} + mouse wheel to zoom; Mouse wheel to scroll`,
|
||||
`<b><u>Toggle ON:</u></b> Mouse wheel to zoom; ${labelCTRL()} + mouse wheel to scroll</br><b><u>Toggle OFF:</u></b>${labelCTRL()} + mouse wheel to zoom; Mouse wheel to scroll`,
|
||||
|
||||
ZOOM_TO_FIT_NAME: "Zoom to fit on view resize",
|
||||
ZOOM_TO_FIT_DESC: "Zoom to fit drawing when the pane is resized" +
|
||||
"<br><b>Toggle ON:</b> Zoom to fit<br><b>Toggle OFF:</b> Auto zoom disabled",
|
||||
"<br><b><u>Toggle ON:</u></b> Zoom to fit<br><b><u>Toggle OFF:</u></b> Auto zoom disabled",
|
||||
ZOOM_TO_FIT_ONOPEN_NAME: "Zoom to fit on file open",
|
||||
ZOOM_TO_FIT_ONOPEN_DESC: "Zoom to fit drawing when the drawing is first opened" +
|
||||
"<br><b>Toggle ON:</b> Zoom to fit<br><b>Toggle OFF:</b> Auto zoom disabled",
|
||||
"<br><b><u>Toggle ON:</u></b> Zoom to fit<br><b><u>Toggle OFF:</u></b> Auto zoom disabled",
|
||||
ZOOM_TO_FIT_MAX_LEVEL_NAME: "Zoom to fit max ZOOM level",
|
||||
ZOOM_TO_FIT_MAX_LEVEL_DESC:
|
||||
"Set the maximum level to which zoom to fit will enlarge the drawing. Minimum is 0.5 (50%) and maximum is 10 (1000%).",
|
||||
@@ -272,9 +273,9 @@ FILENAME_HEAD: "Filename",
|
||||
DONE_DESC: "Icon to use for completed TODO items",
|
||||
HOVERPREVIEW_NAME: `Hover preview without pressing the ${labelCTRL()} key`,
|
||||
HOVERPREVIEW_DESC:
|
||||
`<b>Toggle On</b>: In Exalidraw <u>view mode</u> the hover preview for [[wiki links]] will be shown immediately, without the need to hold the ${labelCTRL()} key. ` +
|
||||
`<b><u>Toggle ON:</u></b> In Exalidraw <u>view mode</u> the hover preview for [[wiki links]] will be shown immediately, without the need to hold the ${labelCTRL()} key. ` +
|
||||
"In Excalidraw <u>normal mode</u>, the preview will be shown immediately only when hovering the blue link icon in the top right of the element.<br> " +
|
||||
`<b>Toggle Off</b>: Hover preview is shown only when you hold the ${labelCTRL()} key while hovering the link.`,
|
||||
`<b><u>Toggle OFF:</u></b> Hover preview is shown only when you hold the ${labelCTRL()} key while hovering the link.`,
|
||||
LINKOPACITY_NAME: "Opacity of link icon",
|
||||
LINKOPACITY_DESC:
|
||||
"Opacity of the link indicator icon in the top right corner of an element. 1 is opaque, 0 is transparent.",
|
||||
@@ -299,7 +300,7 @@ FILENAME_HEAD: "Filename",
|
||||
"![[markdown page]] format.",
|
||||
QUOTE_TRANSCLUSION_REMOVE_NAME: "Quote translusion: remove leading '> ' from each line",
|
||||
QUOTE_TRANSCLUSION_REMOVE_DESC: "Remove the leading '> ' from each line of the transclusion. This will improve readability of quotes in text only transclusions<br>" +
|
||||
"<b>Toggle ON:</b> Remove leading '> '<br><b>Toggle OFF:</b> Do not remove leading '> ' (note it will still be removed from the first row due to Obsidian API functionality)",
|
||||
"<b><u>Toggle ON:</u></b> Remove leading '> '<br><b><u>Toggle OFF:</u></b> Do not remove leading '> ' (note it will still be removed from the first row due to Obsidian API functionality)",
|
||||
GET_URL_TITLE_NAME: "Use iframely to resolve page title",
|
||||
GET_URL_TITLE_DESC:
|
||||
"Use the <code>http://iframely.server.crestify.com/iframely?url=</code> to get title of page when dropping a link into Excalidraw",
|
||||
@@ -363,13 +364,13 @@ FILENAME_HEAD: "Filename",
|
||||
"For a number of reasons, the same approach cannot be used to expedite the loading of drawings with many embedded objects. See demonstration <a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/1.6.23' target='_blank'>here</a>.",
|
||||
/*EMBED_PREVIEW_SVG_NAME: "Display SVG in markdown preview",
|
||||
EMBED_PREVIEW_SVG_DESC:
|
||||
"<b>Toggle ON</b>: Embed drawing as an <a href='https://en.wikipedia.org/wiki/Scalable_Vector_Graphics' target='_blank'>SVG</a> image into the markdown preview.<br>" +
|
||||
"<b>Toggle OFF</b>: Embed drawing as a <a href='' target='_blank'>PNG</a> image. Note, that some of the <a href='https://www.youtube.com/watch?v=yZQoJg2RCKI&t=633s' target='_blank'>image block referencing features</a> do not work with PNG embeds.",*/
|
||||
"<b><u>Toggle ON:</u></b> Embed drawing as an <a href='https://en.wikipedia.org/wiki/Scalable_Vector_Graphics' target='_blank'>SVG</a> image into the markdown preview.<br>" +
|
||||
"<b><u>Toggle OFF:</u></b> Embed drawing as a <a href='' target='_blank'>PNG</a> image. Note, that some of the <a href='https://www.youtube.com/watch?v=yZQoJg2RCKI&t=633s' target='_blank'>image block referencing features</a> do not work with PNG embeds.",*/
|
||||
EMBED_PREVIEW_IMAGETYPE_NAME: "Image type in markdown preview",
|
||||
EMBED_PREVIEW_IMAGETYPE_DESC:
|
||||
"<b>Native SVG</b>: High Image Quality. Embedded Websites, YouTube videos, Obsidian Links, and external images embedded via a URL will all work. Embedded Obsidian pages will not<br>" +
|
||||
"<b>SVG Image</b>: High Image Quality. Embedded elements and images embedded via URL only have placeholders, links don't work<br>" +
|
||||
"<b>PNG Image</b>: Lower Image Quality, but in some cases better performance with large drawings. Embedded elements and images embedded via URL only have placeholders, links don't work. Also some of the <a href='https://www.youtube.com/watch?v=yZQoJg2RCKI&t=633s' target='_blank'>image block referencing features</a> do not work with PNG embeds.",
|
||||
"<b><u>Native SVG</u></b>: High Image Quality. Embedded Websites, YouTube videos, Obsidian Links, and external images embedded via a URL will all work. Embedded Obsidian pages will not<br>" +
|
||||
"<b><u>SVG Image</u></b>: High Image Quality. Embedded elements and images embedded via URL only have placeholders, links don't work<br>" +
|
||||
"<b><u>PNG Image</u></b>: Lower Image Quality, but in some cases better performance with large drawings. Embedded elements and images embedded via URL only have placeholders, links don't work. Also some of the <a href='https://www.youtube.com/watch?v=yZQoJg2RCKI&t=633s' target='_blank'>image block referencing features</a> do not work with PNG embeds.",
|
||||
PREVIEW_MATCH_OBSIDIAN_NAME: "Excalidraw preview to match Obsidian theme",
|
||||
PREVIEW_MATCH_OBSIDIAN_DESC:
|
||||
"Image preview in documents should match the Obsidian theme. If enabled, when Obsidian is in dark mode, Excalidraw images will render in dark mode. " +
|
||||
@@ -387,7 +388,7 @@ FILENAME_HEAD: "Filename",
|
||||
"This option will not autogenerate PNG/SVG files, but will simply reference the already existing files.",
|
||||
EMBED_WIKILINK_NAME: "Embed Drawing using Wiki link",
|
||||
EMBED_WIKILINK_DESC:
|
||||
"Toggle ON: Excalidraw will embed a [[wiki link]]. Toggle OFF: Excalidraw will embed a [markdown](link).",
|
||||
"<b><u>Toggle ON:</u></b> Excalidraw will embed a [[wiki link]].<br><b><u>Toggle OFF:</u></b> Excalidraw will embed a [markdown](link).",
|
||||
EXPORT_PNG_SCALE_NAME: "PNG export image scale",
|
||||
EXPORT_PNG_SCALE_DESC: "The size-scale of the exported PNG image",
|
||||
EXPORT_BACKGROUND_NAME: "Export image with background",
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS,
|
||||
FRONTMATTER_KEY_CUSTOM_PREFIX,
|
||||
FRONTMATTER_KEY_CUSTOM_URL_PREFIX,
|
||||
} from "src/Constants";
|
||||
} from "src/constants";
|
||||
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/ModifierkeyHelper";
|
||||
|
||||
// 简体中文
|
||||
@@ -19,80 +19,86 @@ export default {
|
||||
"脚本已是最新 - 点击重新安装",
|
||||
OPEN_AS_EXCALIDRAW: "打开为 Excalidraw 绘图",
|
||||
TOGGLE_MODE: "在 Excalidraw 和 Markdown 模式之间切换",
|
||||
CONVERT_NOTE_TO_EXCALIDRAW: "转换空白笔记为 Excalidraw 绘图",
|
||||
CONVERT_EXCALIDRAW: "转换 *.excalidraw 为 *.md 文件",
|
||||
CREATE_NEW: "新建 Excalidraw 绘图",
|
||||
CONVERT_FILE_KEEP_EXT: "*.excalidraw => *.excalidraw.md",
|
||||
CONVERT_FILE_REPLACE_EXT: "*.excalidraw => *.md (兼容 Logseq)",
|
||||
CONVERT_NOTE_TO_EXCALIDRAW: "转换:空白 Markdown 文档 => Excalidraw 绘图文件",
|
||||
CONVERT_EXCALIDRAW: "转换: *.excalidraw => *.md",
|
||||
CREATE_NEW: "新建绘图文件",
|
||||
CONVERT_FILE_KEEP_EXT: "转换:*.excalidraw => *.excalidraw.md",
|
||||
CONVERT_FILE_REPLACE_EXT: "转换:*.excalidraw => *.md (兼容 Logseq)",
|
||||
DOWNLOAD_LIBRARY: "导出 stencil 库为 *.excalidrawlib 文件",
|
||||
OPEN_EXISTING_NEW_PANE: "打开已有的绘图 - 于新面板",
|
||||
OPEN_EXISTING_ACTIVE_PANE:
|
||||
"打开已有的绘图 - 于当前面板",
|
||||
TRANSCLUDE: "嵌入绘图(形如 ![[drawing]])到当前文档",
|
||||
TRANSCLUDE_MOST_RECENT: "嵌入最近编辑过的绘图(形如 ![[drawing]])到当前文档",
|
||||
TRANSCLUDE: "嵌入绘图(形如 ![[drawing]])到当前 Markdown 文档中",
|
||||
TRANSCLUDE_MOST_RECENT: "嵌入最近编辑过的绘图(形如 ![[drawing]])到当前 Markdown 文档中",
|
||||
TOGGLE_LEFTHANDED_MODE: "切换为左手模式",
|
||||
NEW_IN_NEW_PANE: "新建绘图 - 于新面板",
|
||||
NEW_IN_NEW_TAB: "新建绘图 - 于新页签",
|
||||
NEW_IN_ACTIVE_PANE: "新建绘图 - 于当前面板",
|
||||
NEW_IN_POPOUT_WINDOW: "新建绘图 - 于新窗口",
|
||||
NEW_IN_NEW_PANE_EMBED:
|
||||
"新建绘图 - 于新面板 - 并将其嵌入(形如 ![[drawing]])到当前文档",
|
||||
"新建绘图 - 于新面板 - 并将其嵌入(形如 ![[drawing]])到当前 Markdown 文档中",
|
||||
NEW_IN_NEW_TAB_EMBED:
|
||||
"新建绘图 - 于新页签 - 并将其嵌入(形如 ![[drawing]])到当前文档",
|
||||
"新建绘图 - 于新页签 - 并将其嵌入(形如 ![[drawing]])到当前 Markdown 文档中",
|
||||
NEW_IN_ACTIVE_PANE_EMBED:
|
||||
"新建绘图 - 于当前面板 - 并将其嵌入(形如 ![[drawing]])到当前文档",
|
||||
NEW_IN_POPOUT_WINDOW_EMBED: "新建绘图 - 于新窗口 - 并将其嵌入(形如 ![[drawing]])到当前文档",
|
||||
EXPORT_SVG: "导出 SVG 文件到当前目录",
|
||||
EXPORT_PNG: "导出 PNG 文件到当前目录",
|
||||
EXPORT_SVG_WITH_SCENE: "导出 SVG 文件(包含 Scene)到当前目录",
|
||||
EXPORT_PNG_WITH_SCENE: "导出 PNG 文件(包含 Scene)到当前目录",
|
||||
"新建绘图 - 于当前面板 - 并将其嵌入(形如 ![[drawing]])到当前 Markdown 文档中",
|
||||
NEW_IN_POPOUT_WINDOW_EMBED: "新建绘图 - 于新窗口 - 并将其嵌入(形如 ![[drawing]])到当前 Markdown 文档中",
|
||||
TOGGLE_LOCK: "文本元素:原文模式(RAW)⟺ 预览模式(PREVIEW)",
|
||||
DELETE_FILE: "从库中删除所选图像(或 MD-Embed)的源文件",
|
||||
DELETE_FILE: "从库中删除所选图像或 MD-Embed 的源文件",
|
||||
INSERT_LINK_TO_ELEMENT:
|
||||
`复制所选元素的内部链接(形如 [[file#^elementID]])。\n按住 ${labelCTRL()} 可复制元素所在分组的内部链接(形如 [[file#^group=elementID]])。\n按住 ${labelSHIFT()} 可复制所选元素周围区域的内部链接(形如 [[file#^area=elementID]])。\n按住 ${labelALT()} 可观看视频演示。`,
|
||||
`复制所选元素为内部链接(形如 [[file#^id]] )。\n按住 ${labelCTRL()} 可复制元素所在分组为内部链接(形如 [[file#^group=id]] )。\n按住 ${labelSHIFT()} 可复制所选元素所在区域为内部链接(形如 [[file#^area=id]] )。\n按住 ${labelALT()} 可观看视频演示。`,
|
||||
INSERT_LINK_TO_ELEMENT_GROUP:
|
||||
"复制所选元素所在分组的内部链接(形如 [[file#^group=elementID]])",
|
||||
"复制所选元素所在分组为内部链接(形如 [[file#^group=id]] )",
|
||||
INSERT_LINK_TO_ELEMENT_AREA:
|
||||
"复制所选元素周围区域的内部链接(形如 [[file#^area=elementID]])",
|
||||
"复制所选元素所在区域为内部链接(形如 [[file#^area=id]] )",
|
||||
INSERT_LINK_TO_ELEMENT_FRAME:
|
||||
"复制所选框架为内部链接(形如 [[file#^frame=id]] )",
|
||||
INSERT_LINK_TO_ELEMENT_NORMAL:
|
||||
"复制所选元素的内部链接(形如 [[file#^elementID]])",
|
||||
"复制所选元素为内部链接(形如 [[file#^id]] )",
|
||||
INSERT_LINK_TO_ELEMENT_ERROR: "未选择画布里的单个元素",
|
||||
INSERT_LINK_TO_ELEMENT_READY: "链接已生成并复制到剪贴板",
|
||||
INSERT_LINK: "插入文件的内部链接(形如 [[drawing]])到当前绘图",
|
||||
INSERT_IMAGE: "插入图像(以图像形式嵌入)到当前绘图",
|
||||
IMPORT_SVG: "插入 SVG 矢量图形到当前绘图(支持有限,尚不支持文本)",
|
||||
INSERT_MD: "插入 Markdown 文档(以图像形式嵌入)到当前绘图",
|
||||
INSERT_LINK: "插入任意文件(以内部链接形式嵌入,形如 [[drawing]] )到当前绘图中",
|
||||
INSERT_IMAGE: "插入图像或 Excalidraw 绘图(以图像形式嵌入)到当前绘图中",
|
||||
IMPORT_SVG: "从 SVG 文件导入图形元素到当前绘图中(暂不支持文本元素)",
|
||||
INSERT_MD: "插入 Markdown 文档(以图像形式嵌入)到当前绘图中",
|
||||
INSERT_PDF: "插入 PDF 文档(以图像形式嵌入)到当前绘图中",
|
||||
UNIVERSAL_ADD_FILE: "插入任意文件(以 iFrame 形式嵌入)到当前绘图中",
|
||||
INSERT_LATEX:
|
||||
`插入 LaTeX 公式到当前绘图。按住 ${labelALT()} 可观看视频演示。`,
|
||||
ENTER_LATEX: "输入 LaTeX 表达式",
|
||||
READ_RELEASE_NOTES: "阅读本插件的更新说明",
|
||||
RUN_OCR: "OCR 识别涂鸦和图片里的文本并复制到剪贴板",
|
||||
RUN_OCR: "OCR:识别涂鸦和图片里的文本并复制到剪贴板",
|
||||
TRAY_MODE: "绘图工具属性页:面板模式 ⟺ 托盘模式",
|
||||
SEARCH: "搜索文本",
|
||||
RESET_IMG_TO_100: "重设图像元素的尺寸为 100%",
|
||||
TEMPORARY_DISABLE_AUTOSAVE: "临时禁用自动保存功能,直到 Obsidian 退出(勿点,除非你清楚自己在干什么)",
|
||||
TEMPORARY_ENABLE_AUTOSAVE: "恢复启用自动保存功能",
|
||||
TEMPORARY_DISABLE_AUTOSAVE: "临时禁用自动保存功能,直到本次 Obsidian 退出(小白慎用!)",
|
||||
TEMPORARY_ENABLE_AUTOSAVE: "启用自动保存功能",
|
||||
|
||||
//ExcalidrawView.ts
|
||||
INSTALL_SCRIPT_BUTTON: "安装或更新 Excalidraw 脚本",
|
||||
OPEN_AS_MD: "打开为 Markdown 文件",
|
||||
SAVE_AS_PNG: `导出 PNG 到当前目录(按住 ${labelCTRL()} 设定导出路径;按住 SHIFT 在导出时包含 Scene)`,
|
||||
SAVE_AS_SVG: `导出 SVG 到当前目录(按住 ${labelCTRL()} 设定导出路径;按住 SHIFT 在导出时包含 Scene)`,
|
||||
OPEN_AS_MD: "打开为 Markdown 文档",
|
||||
EXPORT_IMAGE: `导出为图像`,
|
||||
OPEN_LINK: "打开所选元素里的链接 \n(按住 SHIFT 在新面板打开)",
|
||||
EXPORT_EXCALIDRAW: "导出为 .Excalidraw 文件",
|
||||
EXPORT_EXCALIDRAW: "导出为 .excalidraw 文件(旧版绘图文件格式)",
|
||||
LINK_BUTTON_CLICK_NO_TEXT:
|
||||
"请选择一个含有链接的图形或文本元素。",
|
||||
FILENAME_INVALID_CHARS:
|
||||
'文件名不能含有以下符号: * " \\ < > : | ? #',
|
||||
FORCE_SAVE:
|
||||
"保存绘图(并更新嵌入了该绘图的面板)",
|
||||
"保存(同时更新嵌入了该绘图的 Markdown 文档)",
|
||||
RAW: "文本元素正以原文(RAW)模式显示链接。\n点击切换到预览(PREVIEW)模式",
|
||||
PARSED:
|
||||
"文本元素正以预览(PREVIEW)模式显示链接。\n点击切换到原文(RAW)模式",
|
||||
NOFILE: "Excalidraw(没有文件)",
|
||||
COMPATIBILITY_MODE:
|
||||
"*.excalidraw 文件正以兼容模式打开。需要转换为新格式才能使用插件的全部功能。",
|
||||
"*.excalidraw 是兼容旧版的绘图文件格式。需要转换为新格式才能解锁本插件的全部功能。",
|
||||
CONVERT_FILE: "转换为新格式",
|
||||
BACKUP_AVAILABLE: "加载绘图文件时出错,可能是由于 Obsidian 在上次保存时意外退出了(手机上更容易发生这种意外)。<br><br><b>好消息:</b>这台设备上存在备份。您是否想要恢复本设备上的备份?<br><br>(我建议您先尝试在最近使用过的其他设备上打开该绘图,以检查是否有更新的备份。)",
|
||||
BACKUP_RESTORED: "已恢复备份",
|
||||
CACHE_NOT_READY: "抱歉,加载绘图文件时出错。<br><br><mark>现在有耐心,将来更省心。</mark><br><br>该插件有备份机制,但您似乎刚刚打开 Obsidian,需要等待一分钟或更长的时间来读取缓存。缓存读取完毕时,您将会在右上角收到提示。<br><br>请点击 OK 并耐心等待缓存,或者选择点击取消后手动修复你的文件。<br>",
|
||||
OBSIDIAN_TOOLS_PANEL: "Obsidian 工具面板",
|
||||
ERROR_SAVING_IMAGE: "获取图像时发生未知错误",
|
||||
WARNING_PASTING_ELEMENT_AS_TEXT: "你不能将 Excalidraw 元素粘贴为文本元素!",
|
||||
USE_INSERT_FILE_MODAL: "使用“插入任意文件(以 iFrame 形式嵌入)”功能来嵌入 Markdown 文档",
|
||||
|
||||
//settings.ts
|
||||
RELEASE_NOTES_NAME: "显示更新说明",
|
||||
@@ -108,10 +114,10 @@ export default {
|
||||
FOLDER_DESC:
|
||||
"新绘图的默认存储路径。若为空,将在库的根目录中创建新绘图。",
|
||||
FOLDER_EMBED_NAME:
|
||||
"将 Excalidraw 文件夹用于“新建绘图”命令创建的绘图",
|
||||
"将 Excalidraw 文件夹用于“新建绘图”系列命令",
|
||||
FOLDER_EMBED_DESC:
|
||||
"在命令面板中执行“新建绘图”系列命令时," +
|
||||
"新绘图的存储路径。<br>" +
|
||||
"新建的绘图文件的存储路径。<br>" +
|
||||
"<b>开启:</b>使用 Excalidraw 文件夹。 <br><b>关闭:</b>使用 Obsidian 设置的新附件默认位置。",
|
||||
TEMPLATE_NAME: "Excalidraw 模板文件",
|
||||
TEMPLATE_DESC:
|
||||
@@ -136,37 +142,37 @@ export default {
|
||||
"当您通过功能区按钮或命令将绘图切换成 Markdown 模式时," +
|
||||
"数据将被解码回 JSON 格式以便阅读和编辑;" +
|
||||
"而当您切换回 Excalidraw 模式时,数据就会被再次编码。<br>" +
|
||||
"开启此项后,对于之前已存在的未压缩的绘图文件," +
|
||||
"需要重新打开并保存它们才能生效。",
|
||||
AUTOSAVE_INTERVAL_DESKTOP_NAME: "桌面端定期保存时间间隔",
|
||||
"开启此项后,对于之前已存在但未压缩的绘图文件," +
|
||||
"需要重新打开并保存才能生效。",
|
||||
AUTOSAVE_INTERVAL_DESKTOP_NAME: "桌面端自动保存时间间隔",
|
||||
AUTOSAVE_INTERVAL_DESKTOP_DESC:
|
||||
"每隔多长时间触发一次自动保存。但如果当前绘图没有发生改变,将不会触发自动保存。" +
|
||||
"当 Obsidian 应用内的焦点离开活动文档(如关闭工作空间、点击菜单栏、切换到其他页签或面板等)的时候,会触发自动保存。" +
|
||||
"每隔多长时间自动保存一次(如果绘图文件没有发生改变,将不会保存)。" +
|
||||
"当 Obsidian 应用内的焦点离开活动文档(如关闭工作空间、点击菜单栏、切换到其他页签或面板等)的时候,也会触发自动保存。" +
|
||||
"直接退出 Obsidian 应用(不管是终结进程还是点关闭按钮)不会触发自动保存。",
|
||||
AUTOSAVE_INTERVAL_MOBILE_NAME: "移动端定期保存时间间隔",
|
||||
AUTOSAVE_INTERVAL_MOBILE_NAME: "移动端自动保存时间间隔",
|
||||
AUTOSAVE_INTERVAL_MOBILE_DESC:
|
||||
"建议在移动端设置更短的自动保存时间间隔。" +
|
||||
"当 Obsidian 应用内的焦点离开活动文档(如关闭工作空间、点击菜单栏、切换到其他页签或面板等)的时候,会触发自动保存。" +
|
||||
"建议在移动端设置更短的时间间隔。" +
|
||||
"当 Obsidian 应用内的焦点离开活动文档(如关闭工作空间、点击菜单栏、切换到其他页签或面板等)的时候,也会触发自动保存。" +
|
||||
"直接退出 Obsidian 应用(在应用切换器中划掉)不会触发自动保存。此外,当您切换到其他应用时,有时候" +
|
||||
"系统会自动清理 Obsidian 后台以释放资源。这种情况下,Excalidraw 无法保存最新的变动。",
|
||||
"系统会自动清理 Obsidian 后台以释放资源。这种情况下,自动保存会失效。",
|
||||
FILENAME_HEAD: "文件名",
|
||||
FILENAME_DESC:
|
||||
"<p>点击阅读" +
|
||||
"<a href='https://momentjs.com/docs/#/displaying/format/'>日期和时间格式参考</a>。</p>",
|
||||
FILENAME_SAMPLE: "“新建绘图”系列命令创建的文件名形如:",
|
||||
FILENAME_EMBED_SAMPLE: "“新建绘图并嵌入到当前文档”系列命令创建的文件名形如:",
|
||||
FILENAME_EMBED_SAMPLE: "“新建绘图并嵌入到当前 Markdown 文档中”系列命令创建的文件名形如:",
|
||||
FILENAME_PREFIX_NAME: "“新建绘图”系列命令创建的文件名前缀",
|
||||
FILENAME_PREFIX_DESC: "执行“新建绘图”系列命令时,创建的绘图文件名的第一部分",
|
||||
FILENAME_PREFIX_EMBED_NAME:
|
||||
"“新建绘图并嵌入到当前文档”系列命令创建的文件名前缀",
|
||||
"“新建绘图并嵌入到当前 Markdown 文档中”系列命令创建的文件名前缀",
|
||||
FILENAME_PREFIX_EMBED_DESC:
|
||||
"执行“新建绘图并嵌入到当前文档”系列命令时," +
|
||||
"执行“新建绘图并嵌入到当前 Markdown 文档中”系列命令时," +
|
||||
"创建的绘图文件名是否以当前文档名作为前缀?<br>" +
|
||||
"<b>开启:</b>是<br><b>关闭:</b>否",
|
||||
FILENAME_POSTFIX_NAME:
|
||||
"“新建绘图并嵌入到当前文档”系列命令创建的文件名的中间部分",
|
||||
"“新建绘图并嵌入到当前 Markdown 文档中”系列命令创建的文件名的中间部分",
|
||||
FILENAME_POSTFIX_DESC:
|
||||
"介于文件名前缀和日期时间之间的文本。仅对“新建绘图并嵌入到当前文档”系列命令创建的绘图生效。",
|
||||
"介于文件名前缀和日期时间之间的文本。仅对“新建绘图并嵌入到当前 Markdown 文档中”系列命令创建的绘图生效。",
|
||||
FILENAME_DATE_NAME: "文件名里的日期时间",
|
||||
FILENAME_DATE_DESC:
|
||||
"文件名的最后一部分。允许留空。",
|
||||
@@ -175,10 +181,18 @@ FILENAME_HEAD: "文件名",
|
||||
"该选项在兼容模式(即非 Excalidraw 专用 Markdown 文件)下不会生效。<br>" +
|
||||
"<b>开启:</b>使用 .excalidraw.md 作为扩展名。<br><b>关闭:</b>使用 .md 作为扩展名。",
|
||||
DISPLAY_HEAD: "显示",
|
||||
DYNAMICSTYLE_NAME: "动态样式",
|
||||
DYNAMICSTYLE_DESC:
|
||||
"根据画布颜色调节 Excalidraw 界面颜色",
|
||||
LEFTHANDED_MODE_NAME: "左手模式",
|
||||
LEFTHANDED_MODE_DESC:
|
||||
"目前只在托盘模式下生效。若开启此项,则托盘(绘图工具属性页)将位于右侧。" +
|
||||
"<br><b>开启:</b>左手模式。<br><b>关闭:</b>右手模式。",
|
||||
IFRAME_MATCH_THEME_NAME: "使 MD-Embed 匹配 Excalidraw 主题",
|
||||
IFRAME_MATCH_THEME_DESC:
|
||||
"<b>开启:</b>当你的 Obsidian 和 Excalidraw 一个使用黑暗主题、一个使用明亮主题时," +
|
||||
"开启此项,MD-Embed 将会匹配 Excalidraw 主题。<br>" +
|
||||
"<b>关闭:</b>如果你想要 MD-Embed 匹配 Obsidian 主题,请关闭此项。",
|
||||
MATCH_THEME_NAME: "使新建的绘图匹配 Obsidian 主题",
|
||||
MATCH_THEME_DESC:
|
||||
"如果 Obsidian 使用黑暗主题,新建的绘图文件也将使用黑暗主题。<br>" +
|
||||
@@ -218,7 +232,7 @@ FILENAME_HEAD: "文件名",
|
||||
ZOOM_TO_FIT_MAX_LEVEL_NAME: "自动缩放的最高级别",
|
||||
ZOOM_TO_FIT_MAX_LEVEL_DESC:
|
||||
"自动缩放画布时,允许放大的最高级别。该值不能低于 0.5(50%)且不能超过 10(1000%)。",
|
||||
LINKS_HEAD: "链接(Links) & 以文本形式嵌入到绘图中的文档(Transclusion)",
|
||||
LINKS_HEAD: "链接(Links) & 以内部链接形式嵌入到绘图中的 Markdown 文档(Transclusion)",
|
||||
LINKS_DESC:
|
||||
`按住 ${labelCTRL()} 并点击包含 <code>[[链接]]</code> 的文本元素可以打开其中的链接。` +
|
||||
"如果所选文本元素包含多个 <code>[[有效的内部链接]]</code> ,只会打开第一个链接;" +
|
||||
@@ -292,8 +306,9 @@ FILENAME_HEAD: "文件名",
|
||||
"拖放链接到 Excalidraw 时,使用 <code>http://iframely.server.crestify.com/iframely?url=</code> 来获取页面的标题。",
|
||||
MD_HEAD: "以图像形式嵌入到绘图中的 Markdown 文档(MD-Embed)",
|
||||
MD_HEAD_DESC:
|
||||
"您还可以将 Markdown 文档以图像形式(而非文本形式)嵌入到绘图中。" +
|
||||
"除了 Transclusion,您还可以将 Markdown 文档以图像形式嵌入到绘图中。" +
|
||||
`方法是按住 ${labelCTRL()} 并从文件管理器中把文档拖入绘图,或者执行“以图像形式嵌入”系列命令。`,
|
||||
|
||||
MD_TRANSCLUDE_WIDTH_NAME: "MD-Embed 的默认宽度",
|
||||
MD_TRANSCLUDE_WIDTH_DESC:
|
||||
"MD-Embed 的宽度。该选项会影响到折行,以及图像元素的宽度。<br>" +
|
||||
@@ -329,35 +344,49 @@ FILENAME_HEAD: "文件名",
|
||||
"此外,在 CSS 中不能任意地设置字体,您一般只能使用系统默认的标准字体(详见 README)," +
|
||||
"但可以通过上面的设置来额外添加一个自定义字体。<br>" +
|
||||
"您可为某个 MD-Embed 单独设置此项,方法是在其源文件的 frontmatter 中添加形如 <code>excalidraw-css: 库中的CSS文件或CSS片段</code> 的键值对。",
|
||||
EMBED_HEAD: "嵌入到文档中的绘图(Embed) & 导出",
|
||||
EMBED_HEAD: "嵌入到 Markdown 文档中的绘图 & 导出",
|
||||
EMBED_CACHING: "启用预览图",
|
||||
EMBED_SIZING: "预览图的尺寸",
|
||||
EMBED_THEME_BACKGROUND: "预览图的主题和背景色",
|
||||
EMBED_IMAGE_CACHE_NAME: "为嵌入到 Markdown 文档中的绘图创建预览图",
|
||||
EMBED_IMAGE_CACHE_DESC: "为嵌入到文档中的绘图创建预览图。可提高下次嵌入的速度。" +
|
||||
"但如果绘图中又嵌入了子绘图,当子绘图改变时,您需要打开子绘图并手动保存,才能够更新父绘图的预览图。",
|
||||
EMBED_IMAGE_CACHE_CLEAR: "清除预览图",
|
||||
BACKUP_CACHE_CLEAR: "清除备份",
|
||||
BACKUP_CACHE_CLEAR_CONFIRMATION: "该操作将删除所有绘图文件的备份。备份是绘图文件损坏时的一种补救手段。每次您打开 Obsidian 时,本插件会自动清理无用的备份。您确定要删除所有备份吗?",
|
||||
EMBED_REUSE_EXPORTED_IMAGE_NAME:
|
||||
"将之前已导出的图像作为 Embed 的预览图(如果存在的话)",
|
||||
"将之前已导出的图像作为预览图",
|
||||
EMBED_REUSE_EXPORTED_IMAGE_DESC:
|
||||
"该选项与“自动导出 SVG/PNG 副本”选项配合使用。如果存在文件名相匹配的 SVG/PNG 副本,则将其作为 Embed 的预览图,而不再重新生成预览图。<br>" +
|
||||
"该选项能够提高性能,尤其是当 Embed 中含有大量图像或 MD-Embed 时。" +
|
||||
"但是,该选项也可能导致预览图无法立即响应你最新的修改,或者你对 Obsidian 主题风格的改变。<br>" +
|
||||
"该选项仅作用于嵌入到文档中的绘图。" +
|
||||
"由于种种原因,该技术无法用于加快绘图文件的打开速度。详见<a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/1.6.23' target='_blank'>此说明</a>。",
|
||||
EMBED_PREVIEW_SVG_NAME: "生成 SVG 格式的 Embed 预览图",
|
||||
"该选项与“自动导出 SVG/PNG 副本”选项配合使用。如果嵌入到 Markdown 文档中的绘图文件存在同名的 SVG/PNG 副本,则将其作为预览图,而不再重新生成。<br>" +
|
||||
"该选项能够提高 Markdown 文档的打开速度,尤其是当嵌入到 Markdown 文档中的绘图文件中含有大量图像或 MD-Embed 时。" +
|
||||
"但是,该选项也可能导致预览图无法立即响应你对绘图文件或者 Obsidian 主题风格的修改。<br>" +
|
||||
"该选项仅作用于嵌入到 Markdown 文档中的绘图。" +
|
||||
"该选项无法提升绘图文件的打开速度。详见<a href='https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/1.6.23' target='_blank'>此说明</a>。",
|
||||
/*EMBED_PREVIEW_SVG_NAME: "生成 SVG 格式的预览图",
|
||||
EMBED_PREVIEW_SVG_DESC:
|
||||
"<b>开启:</b>在 Markdown 预览模式下,为 Embed 生成 <a href='https://en.wikipedia.org/wiki/Scalable_Vector_Graphics' target='_blank'>SVG</a> 格式的预览图。<br>" +
|
||||
"<b>关闭:</b>为 Embed 生成 <a href='' target='_blank'>PNG</a> 格式的预览图。注意:PNG 格式预览图不支持某些 <a href='https://www.youtube.com/watch?v=yZQoJg2RCKI&t=633s' target='_blank'>绘图元素的块引用特性</a>。",
|
||||
PREVIEW_MATCH_OBSIDIAN_NAME: "Embed 预览图匹配 Obsidian 主题",
|
||||
"<b>开启:</b>为嵌入到 Markdown 文档中的绘图生成 <a href='https://en.wikipedia.org/wiki/Scalable_Vector_Graphics' target='_blank'>SVG</a> 格式的预览图。<br>" +
|
||||
"<b>关闭:</b>为嵌入到 Markdown 文档中的绘图生成 <a href='' target='_blank'>PNG</a> 格式的预览图。注意:PNG 格式预览图不支持某些 <a href='https://www.youtube.com/watch?v=yZQoJg2RCKI&t=633s' target='_blank'>绘图元素的块引用特性</a>。",*/
|
||||
EMBED_PREVIEW_IMAGETYPE_NAME: "预览图的格式",
|
||||
EMBED_PREVIEW_IMAGETYPE_DESC:
|
||||
"<b>原始 SVG:</b>高品质、可交互。<br>" +
|
||||
"<b>SVG:</b>高品质、不可交互。<br>" +
|
||||
"<b>PNG:</b>高性能、<a href='https://www.youtube.com/watch?v=yZQoJg2RCKI&t=633s' target='_blank'>不可交互</a>。",
|
||||
PREVIEW_MATCH_OBSIDIAN_NAME: "预览图匹配 Obsidian 主题",
|
||||
PREVIEW_MATCH_OBSIDIAN_DESC:
|
||||
"开启此项,则当 Obsidian 处于黑暗模式时,Embed 的预览图也会以黑暗模式渲染;当 Obsidian 处于明亮模式时,预览图也会以明亮模式渲染。<br>" +
|
||||
"开启此项,则当 Obsidian 处于黑暗模式时,嵌入到 Markdown 文档中的绘图的预览图也会以黑暗模式渲染;当 Obsidian 处于明亮模式时,预览图也会以明亮模式渲染。<br>" +
|
||||
"您可能还需要关闭“导出的图像包含背景”开关,来获得与 Obsidian 更加协调的观感。",
|
||||
EMBED_WIDTH_NAME: "Embed 预览图的默认宽度",
|
||||
EMBED_WIDTH_NAME: "预览图的默认宽度",
|
||||
EMBED_WIDTH_DESC:
|
||||
"该选项同时作用于 Obsidian 实时预览模式下的编辑视图和阅读视图,以及鼠标悬停时浮现的预览图。<br>" +
|
||||
"您可为某个要嵌入到文档中的绘图(Embed)单独设置此项," +
|
||||
"方法是修改相应的链接格式为形如 <code>![[drawing.excalidraw|100]]</code> 或 <code>[[drawing.excalidraw|100x100]]</code> 的格式。",
|
||||
EMBED_TYPE_NAME: "“嵌入绘图到当前文档”系列命令的源文件类型",
|
||||
"嵌入到 Markdown 文档中的绘图的预览图的默认宽度。该选项也适用于鼠标悬停时浮现的预览图。<br>" +
|
||||
"您可为某个要嵌入到 Markdown 文档中的绘图文件单独设置此项," +
|
||||
"方法是修改相应的内部链接格式为形如 <code>![[drawing.excalidraw|100]]</code> 或 <code>[[drawing.excalidraw|100x100]]</code>。",
|
||||
EMBED_TYPE_NAME: "“嵌入绘图到当前 Markdown 文档中”系列命令的源文件类型",
|
||||
EMBED_TYPE_DESC:
|
||||
"在命令面板中执行“嵌入绘图到当前文档”系列命令时,要嵌入绘图文件本身,还是嵌入其 PNG 或 SVG 副本。<br>" +
|
||||
"如果您想选择 PNG 或 SVG 副本,需要先开启下方的“自动导出 PNG 副本”或“自动导出 SVG 副本”开关。<br>" +
|
||||
"在命令面板中执行“嵌入绘图到当前 Markdown 文档中”系列命令时,要嵌入绘图文件本身,还是嵌入其 PNG 或 SVG 副本。<br>" +
|
||||
"如果您想选择 PNG 或 SVG 副本,需要先开启下方的“自动导出 PNG 副本”或“自动导出 SVG 副本”。<br>" +
|
||||
"如果您选择了 PNG 或 SVG 副本,当副本不存在时,该命令将会插入一条损坏的链接,您需要打开绘图文件并手动导出副本才能修复 —— " +
|
||||
"也就是说,该选项不会自动帮您生成 PNG/SVG 副本,而只会引用已有的 PNG/SVG 副本。",
|
||||
EMBED_WIKILINK_NAME: "“嵌入绘图到当前文档”命令产生的内部链接类型",
|
||||
EMBED_WIKILINK_NAME: "“嵌入绘图到当前 Markdown 文档中”系列命令产生的内部链接类型",
|
||||
EMBED_WIKILINK_DESC:
|
||||
"<b>开启:</b>将产生 <code>![[Wiki 链接]]</code>。<b>关闭:</b>将产生 <code></code>。",
|
||||
EXPORT_PNG_SCALE_NAME: "导出的 PNG 图像的比例",
|
||||
@@ -370,7 +399,7 @@ FILENAME_HEAD: "文件名",
|
||||
"导出的 SVG/PNG 图像四周的空白边距(单位:像素)。<br>" +
|
||||
"增加该值,可以避免在导出图像时,靠近图像边缘的图形被裁掉。<br>" +
|
||||
"您可为某个绘图单独设置此项,方法是在其 frontmatter 中添加形如 <code>excalidraw-export-padding: 5<code> 的键值对。",
|
||||
EXPORT_THEME_NAME: "导出的图像包含主题",
|
||||
EXPORT_THEME_NAME: "导出的图像匹配主题",
|
||||
EXPORT_THEME_DESC:
|
||||
"导出与绘图的黑暗/明亮主题匹配的图像。" +
|
||||
"如果关闭,在黑暗主题下导出的图像将和明亮主题一样。",
|
||||
@@ -390,7 +419,7 @@ FILENAME_HEAD: "文件名",
|
||||
"的键值对",
|
||||
EXPORT_PNG_NAME: "自动导出 PNG 副本",
|
||||
EXPORT_PNG_DESC: "和“自动导出 SVG 副本”类似,但是导出格式为 *.PNG。",
|
||||
EXPORT_BOTH_DARK_AND_LIGHT_NAME: "同时导出黑暗和明亮风格的图像",
|
||||
EXPORT_BOTH_DARK_AND_LIGHT_NAME: "同时导出黑暗和明亮主题风格的图像",
|
||||
EXPORT_BOTH_DARK_AND_LIGHT_DESC: "若开启,Excalidraw 将导出两个文件:filename.dark.png(或 filename.dark.svg)和 filename.light.png(或 filename.light.svg)。<br>"+
|
||||
"该选项可作用于“自动导出 SVG 副本”、“自动导出 PNG 副本”,以及其他的手动的导出命令。",
|
||||
COMPATIBILITY_HEAD: "兼容性设置",
|
||||
@@ -405,11 +434,13 @@ FILENAME_HEAD: "文件名",
|
||||
COMPATIBILITY_MODE_DESC:
|
||||
"开启此功能后,您通过功能区按钮、命令面板、" +
|
||||
"文件浏览器等创建的绘图都将是旧格式(*.excalidraw)。" +
|
||||
"此外,您打开旧格式绘图文件时将不再收到提醒消息。",
|
||||
"此外,您打开旧格式绘图文件时将不再收到警告消息。",
|
||||
MATHJAX_NAME: "MathJax (LaTeX) 的 javascript 库服务器",
|
||||
MATHJAX_DESC: "如果您在绘图中使用 LaTeX,插件需要从服务器获取并加载一个 javascript 库。" +
|
||||
"如果您的网络无法访问某些库服务器,可以尝试通过此选项更换库服务器。"+
|
||||
"更改此选项后,您可能需要重启 Obsidian 来使其生效。",
|
||||
LATEX_DEFAULT_NAME: "插入 LaTeX 时的默认表达式",
|
||||
LATEX_DEFAULT_DESC: "允许留空。允许使用类似 <code>\\color{white}</code> 的格式化表达式。",
|
||||
NONSTANDARD_HEAD: "非 Excalidraw.com 官方支持的特性",
|
||||
NONSTANDARD_DESC: "这些特性不受 Excalidraw.com 官方支持。当导出绘图到 Excalidraw.com 时,这些特性将会发生变化。",
|
||||
CUSTOM_PEN_NAME: "自定义画笔的数量",
|
||||
@@ -447,26 +478,29 @@ FILENAME_HEAD: "文件名",
|
||||
TASKBONE_DESC: "这是一个将 OCR 融入 Excalidraw 的实验性功能。请注意,Taskbone 是一项独立的外部服务,而不是由 Excalidraw 或 Obsidian-excalidraw-plugin 项目提供的。" +
|
||||
"OCR 能够对画布上用自由画笔工具写下的涂鸦或者嵌入的图像进行文本识别,并将识别出来的文本写入绘图文件的 frontmatter,同时复制到剪贴板。" +
|
||||
"之所以要写入 frontmatter 是为了便于您在 Obsidian 中能够搜索到这些文本。" +
|
||||
"注意,识别的过程不是在本地进行的,而是通过在线 API,图像会被上传到 taskbone 的服务器(仅用于识别目的)。如果您对此敏感,请不要使用这个功能。",
|
||||
"注意,识别的过程不是在本地进行的,而是通过在线 API,图像会被上传到 taskbone 的服务器(仅用于识别目的)。如果您介意,请不要使用这个功能。",
|
||||
TASKBONE_ENABLE_NAME: "启用 Taskbone",
|
||||
TASKBONE_ENABLE_DESC: "启用这个功能意味着你同意 Taskbone <a href='https://www.taskbone.com/legal/terms/' target='_blank'>条款及细则</a> 以及 " +
|
||||
"<a href='https://www.taskbone.com/legal/privacy/' target='_blank'>隐私政策</a>.",
|
||||
TASKBONE_APIKEY_NAME: "Taskbone API Key",
|
||||
TASKBONE_APIKEY_DESC: "Taskbone 的免费 API key 提供了一定数量的每月识别次数。如果您非常频繁地使用此功能,或者想要支持 " +
|
||||
"Taskbone 的开发者(您懂的,没有人能用爱发电,Taskbone 开发者也需要投入资金才能持续运行这项 OCR 服务)您可以" +
|
||||
"Taskbone 的开发者(您懂的,没有人能用爱发电,Taskbone 开发者也需要投入资金来维持这项 OCR 服务)您可以" +
|
||||
"到 <a href='https://www.taskbone.com/' target='_blank'>taskbone.com</a> 购买一个商用 API key。购买后请将它填写到旁边这个文本框里,替换掉原本自动生成的免费 API key。",
|
||||
|
||||
//openDrawings.ts
|
||||
SELECT_FILE: "选择一个文件后按回车。",
|
||||
SELECT_FILE_WITH_OPTION_TO_SCALE: `选择一个文件后按回车,或者 ${labelSHIFT()}+${labelMETA()}+ENTER 以 100% 尺寸插入。`,
|
||||
NO_MATCH: "查询不到匹配的文件。",
|
||||
SELECT_FILE_TO_LINK: "选择要插入(链接)到当前绘图中的文件。",
|
||||
SELECT_DRAWING: "选择要插入(以图像形式嵌入)到当前绘图中的图像。",
|
||||
SELECT_FILE_TO_LINK: "选择要插入(以内部链接形式嵌入)到当前绘图中的文件。",
|
||||
SELECT_DRAWING: "选择要插入(以图像形式嵌入)到当前绘图中的图像或绘图文件。",
|
||||
TYPE_FILENAME: "键入要选择的绘图名称。",
|
||||
SELECT_FILE_OR_TYPE_NEW:
|
||||
"选择已有绘图,或者新绘图的类型,然后按回车。",
|
||||
SELECT_TO_EMBED: "选择要插入(嵌入)到当前文档中的绘图。",
|
||||
"选择已有绘图,或者键入新绘图文件的名称,然后按回车。",
|
||||
SELECT_TO_EMBED: "选择要插入(嵌入)到当前 Markdown 文档中的绘图。",
|
||||
SELECT_MD: "选择要插入(以图像形式嵌入)到当前绘图中的 Markdown 文档。",
|
||||
SELECT_PDF: "选择要插入(以图像形式嵌入)到当前绘图中的 PDF 文档。",
|
||||
PDF_PAGES_HEADER: "页码范围",
|
||||
PDF_PAGES_DESC: "示例:1, 3-5, 7, 9-11",
|
||||
|
||||
//EmbeddedFileLoader.ts
|
||||
INFINITE_LOOP_WARNING:
|
||||
@@ -483,6 +517,34 @@ FILENAME_HEAD: "文件名",
|
||||
GOTO_FULLSCREEN: "进入全屏模式",
|
||||
EXIT_FULLSCREEN: "退出全屏模式",
|
||||
TOGGLE_FULLSCREEN: "切换全屏模式",
|
||||
TOGGLE_DISABLEBINDING: "开启或关闭绑定",
|
||||
TOGGLE_FRAME_RENDERING: "开启或关闭框架渲染",
|
||||
TOGGLE_FRAME_CLIPPING: "开启或关闭框架裁剪",
|
||||
OPEN_LINK_CLICK: "打开所选的图形或文本元素里的链接",
|
||||
OPEN_LINK_PROPS: "编辑所选 MD-Embed 的内部链接,或者打开所选的图形或文本元素里的链接"
|
||||
OPEN_LINK_PROPS: "编辑所选 MD-Embed 的内部链接,或者打开所选的图形或文本元素里的链接",
|
||||
|
||||
//IFrameActionsMenu.tsx
|
||||
NARROW_TO_HEADING: "缩放至标题",
|
||||
NARROW_TO_BLOCK: "缩放至块",
|
||||
SHOW_ENTIRE_FILE: "显示全部",
|
||||
ZOOM_TO_FIT: "缩放至合适大小",
|
||||
RELOAD: "重载",
|
||||
OPEN_IN_BROWSER: "在浏览器中打开",
|
||||
|
||||
//Prompts.ts
|
||||
PROMPT_FILE_DOES_NOT_EXIST: "文件不存在。要创建吗?",
|
||||
PROMPT_ERROR_NO_FILENAME: "错误:文件名不能为空",
|
||||
PROMPT_ERROR_DRAWING_CLOSED: "未知错误。绘图文件可能已关闭或丢失",
|
||||
PROMPT_TITLE_NEW_FILE: "新建文件",
|
||||
PROMPT_TITLE_CONFIRMATION: "确认",
|
||||
PROMPT_BUTTON_CREATE_EXCALIDRAW: "创建 Excalidraw 绘图",
|
||||
PROMPT_BUTTON_CREATE_MARKDOWN: "创建 Markdown 文档",
|
||||
PROMPT_BUTTON_NEVERMIND: "算了",
|
||||
PROMPT_BUTTON_OK: "OK",
|
||||
PROMPT_BUTTON_CANCEL: "取消",
|
||||
PROMPT_BUTTON_INSERT_LINE: "插入一行",
|
||||
PROMPT_BUTTON_INSERT_SPACE: "插入空格",
|
||||
PROMPT_BUTTON_INSERT_LINK: "插入内部链接",
|
||||
PROMPT_BUTTON_UPPERCASE: "大写",
|
||||
|
||||
};
|
||||
|
||||
73
src/main.ts
@@ -18,6 +18,8 @@ import {
|
||||
FrontMatterCache,
|
||||
Command,
|
||||
Workspace,
|
||||
Editor,
|
||||
MarkdownFileInfo,
|
||||
} from "obsidian";
|
||||
import {
|
||||
BLANK_DRAWING,
|
||||
@@ -39,7 +41,7 @@ import {
|
||||
EXPORT_TYPES,
|
||||
EXPORT_IMG_ICON_NAME,
|
||||
EXPORT_IMG_ICON,
|
||||
} from "./Constants";
|
||||
} from "./constants";
|
||||
import ExcalidrawView, { TextMode, getTextMode } from "./ExcalidrawView";
|
||||
import {
|
||||
changeThemeOfExcalidrawMD,
|
||||
@@ -86,7 +88,7 @@ import {
|
||||
} from "./utils/Utils";
|
||||
import { getAttachmentsFolderAndFilePath, getNewOrAdjacentLeaf, getParentOfClass, isObsidianThemeDark } from "./utils/ObsidianUtils";
|
||||
//import { OneOffs } from "./OneOffs";
|
||||
import { ExcalidrawImageElement, FileId } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { ExcalidrawElement, ExcalidrawImageElement, FileId } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { ScriptEngine } from "./Scripts";
|
||||
import {
|
||||
hoverEvent,
|
||||
@@ -107,6 +109,7 @@ import { InsertPDFModal } from "./dialogs/InsertPDFModal";
|
||||
import { ExportDialog } from "./dialogs/ExportDialog";
|
||||
import { UniversalInsertFileModal } from "./dialogs/UniversalInsertFileModal";
|
||||
import { imageCache } from "./utils/ImageCache";
|
||||
import { StylesManager } from "./utils/StylesManager";
|
||||
|
||||
declare const EXCALIDRAW_PACKAGES:string;
|
||||
declare const react:any;
|
||||
@@ -152,6 +155,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
public leafChangeTimeout: NodeJS.Timeout = null;
|
||||
private forceSaveCommand:Command;
|
||||
private removeEventLisnters:(()=>void)[] = [];
|
||||
private stylesManager:StylesManager;
|
||||
|
||||
constructor(app: App, manifest: PluginManifest) {
|
||||
super(app, manifest);
|
||||
@@ -212,6 +216,8 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
//https://github.com/mgmeyers/obsidian-kanban/blob/44118e25661bff9ebfe54f71ae33805dc88ffa53/src/main.ts#L267
|
||||
this.registerMonkeyPatches();
|
||||
|
||||
this.stylesManager = new StylesManager(this);
|
||||
|
||||
// const patches = new OneOffs(this);
|
||||
if (this.settings.showReleaseNotes) {
|
||||
//I am repurposing imageElementNotice, if the value is true, this means the plugin was just newly installed to Obsidian.
|
||||
@@ -1701,6 +1707,54 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
private registerEventListeners() {
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(async () => {
|
||||
const onPasteHandler = (
|
||||
evt: ClipboardEvent,
|
||||
editor: Editor,
|
||||
info: MarkdownView | MarkdownFileInfo
|
||||
) => {
|
||||
if(evt.defaultPrevented) return
|
||||
const data = evt.clipboardData.getData("text/plain");
|
||||
if (!data) return;
|
||||
if (data.startsWith(`{"type":"excalidraw/clipboard"`)) {
|
||||
evt.preventDefault();
|
||||
try {
|
||||
const drawing = JSON.parse(data);
|
||||
const hasOneTextElement = drawing.elements.filter((el:ExcalidrawElement)=>el.type==="text").length === 1;
|
||||
if (!(hasOneTextElement || drawing.elements?.length === 1)) {
|
||||
return;
|
||||
}
|
||||
const element = hasOneTextElement
|
||||
? drawing.elements.filter((el:ExcalidrawElement)=>el.type==="text")[0]
|
||||
: drawing.elements[0];
|
||||
if (element.type === "image") {
|
||||
const fileinfo = self.filesMaster.get(element.fileId);
|
||||
if(fileinfo && fileinfo.path) {
|
||||
let path = fileinfo.path;
|
||||
const sourceFile = info.file;
|
||||
const imageFile = self.app.vault.getAbstractFileByPath(path);
|
||||
if(sourceFile && imageFile && imageFile instanceof TFile) {
|
||||
path = self.app.metadataCache.fileToLinktext(imageFile,sourceFile.path);
|
||||
}
|
||||
//@ts-ignore
|
||||
editor.insertText(self.getLink({path}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (element.type === "text") {
|
||||
//@ts-ignore
|
||||
editor.insertText(element.text);
|
||||
return;
|
||||
}
|
||||
if (element.link) {
|
||||
//@ts-ignore
|
||||
editor.insertText(`${element.link}`);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
self.registerEvent(self.app.workspace.on('editor-paste', onPasteHandler));
|
||||
|
||||
//watch filename change to rename .svg, .png; to sync to .md; to update links
|
||||
const renameEventHandler = async (
|
||||
@@ -2087,6 +2141,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
|
||||
onunload() {
|
||||
this.stylesManager.unload();
|
||||
this.removeEventLisnters.forEach((removeEventListener) =>
|
||||
removeEventListener(),
|
||||
);
|
||||
@@ -2123,6 +2178,14 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
})
|
||||
}
|
||||
|
||||
public getLink(
|
||||
{ embed = true, path, alias }: { embed?: boolean; path: string; alias?: string }
|
||||
):string {
|
||||
return this.settings.embedWikiLink
|
||||
? `${embed ? "!" : ""}[[${path}${alias ? `|${alias}` : ""}]]`
|
||||
: `${embed ? "!" : ""}[${alias ?? ""}](${encodeURI(path)})`
|
||||
}
|
||||
|
||||
public async embedDrawing(file: TFile) {
|
||||
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
if (activeView && activeView.file) {
|
||||
@@ -2136,9 +2199,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
//embed Excalidraw
|
||||
if (this.settings.embedType === "excalidraw") {
|
||||
editor.replaceSelection(
|
||||
this.settings.embedWikiLink
|
||||
? `![[${excalidrawRelativePath}]]`
|
||||
: `})`,
|
||||
this.getLink({path: excalidrawRelativePath}),
|
||||
);
|
||||
editor.focus();
|
||||
return;
|
||||
@@ -2202,7 +2263,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
if(!this.settings.previewImageType) { //migration 1.9.13
|
||||
if(typeof this.settings.displaySVGInPreview === "undefined") {
|
||||
this.settings.previewImageType = PreviewImageType.SVG;
|
||||
this.settings.previewImageType = PreviewImageType.SVGIMG;
|
||||
} else {
|
||||
this.settings.previewImageType = this.settings.displaySVGInPreview
|
||||
? PreviewImageType.SVGIMG
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ActionButton } from "./ActionButton";
|
||||
import { ICONS } from "./ActionIcons";
|
||||
import { t } from "src/lang/helpers";
|
||||
import { ScriptEngine } from "src/Scripts";
|
||||
import { ROOTELEMENTSIZE, mutateElement, nanoid, sceneCoordsToViewportCoords } from "src/Constants";
|
||||
import { ROOTELEMENTSIZE, mutateElement, nanoid, sceneCoordsToViewportCoords } from "src/constants";
|
||||
import { REGEX_LINK, REG_LINKINDEX_HYPERLINK } from "src/ExcalidrawData";
|
||||
import { processLinkText, useDefaultExcalidrawFrame } from "src/utils/CustomEmbeddableUtils";
|
||||
import { cleanSectionHeading } from "src/utils/ObsidianUtils";
|
||||
@@ -233,7 +233,11 @@ export class EmbeddableMenu {
|
||||
key={"Open"}
|
||||
title={t("OPEN_IN_BROWSER")}
|
||||
action={() => {
|
||||
view.openExternalLink(iframe.src);
|
||||
view.openExternalLink(
|
||||
!iframe.src.startsWith("https://www.youtube.com") && !iframe.src.startsWith("https://player.vimeo.com")
|
||||
? iframe.src
|
||||
: element.link
|
||||
);
|
||||
}}
|
||||
icon={ICONS.Globe}
|
||||
view={view}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { AppState, ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/t
|
||||
import clsx from "clsx";
|
||||
import { TFile } from "obsidian";
|
||||
import * as React from "react";
|
||||
import { VIEW_TYPE_EXCALIDRAW } from "src/Constants";
|
||||
import { VIEW_TYPE_EXCALIDRAW } from "src/constants";
|
||||
import { PenSettingsModal } from "src/dialogs/PenSettingsModal";
|
||||
import ExcalidrawView from "src/ExcalidrawView";
|
||||
import { PenStyle } from "src/PenTypes";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Notice, TFile } from "obsidian";
|
||||
import * as React from "react";
|
||||
import { ActionButton } from "./ActionButton";
|
||||
import { ICONS, saveIcon, stringToSVG } from "./ActionIcons";
|
||||
import { DEVICE, SCRIPT_INSTALL_FOLDER, VIEW_TYPE_EXCALIDRAW } from "../Constants";
|
||||
import { DEVICE, SCRIPT_INSTALL_FOLDER, VIEW_TYPE_EXCALIDRAW } from "../constants";
|
||||
import { insertLaTeXToView, search } from "../ExcalidrawAutomate";
|
||||
import ExcalidrawView, { TextMode } from "../ExcalidrawView";
|
||||
import { t } from "../lang/helpers";
|
||||
|
||||
1
src/polybooljs.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module "polybooljs";
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
TextComponent,
|
||||
TFile,
|
||||
} from "obsidian";
|
||||
import { GITHUB_RELEASES, VIEW_TYPE_EXCALIDRAW } from "./Constants";
|
||||
import { GITHUB_RELEASES, VIEW_TYPE_EXCALIDRAW } from "./constants";
|
||||
import ExcalidrawView from "./ExcalidrawView";
|
||||
import { t } from "./lang/helpers";
|
||||
import type ExcalidrawPlugin from "./main";
|
||||
@@ -1505,7 +1505,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
d.addOption("Virgil", "Virgil");
|
||||
this.app.vault
|
||||
.getFiles()
|
||||
.filter((f) => ["ttf", "woff", "woff2"].contains(f.extension))
|
||||
.filter((f) => ["ttf", "woff", "woff2", "otf"].contains(f.extension))
|
||||
.forEach((f: TFile) => {
|
||||
d.addOption(f.path, f.name);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { GITHUB_RELEASES } from "src/Constants";
|
||||
import { GITHUB_RELEASES } from "src/constants";
|
||||
import { ExcalidrawGenericElement } from "./ExcalidrawElement";
|
||||
|
||||
declare const PLUGIN_VERSION:string;
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import { getTransformMatrix, transformPoints } from "./transform";
|
||||
import { pointsOnPath } from "points-on-path";
|
||||
import { randomId, getWindingOrder } from "./utils";
|
||||
import { ROUNDNESS } from "../Constants";
|
||||
import { ROUNDNESS } from "../constants";
|
||||
|
||||
const SUPPORTED_TAGS = [
|
||||
"svg",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NonDeletedExcalidrawElement } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { DEVICE, REG_LINKINDEX_INVALIDCHARS } from "src/Constants";
|
||||
import { DEVICE, REG_LINKINDEX_INVALIDCHARS } from "src/constants";
|
||||
import { getParentOfClass } from "./ObsidianUtils";
|
||||
import { TFile, WorkspaceLeaf } from "obsidian";
|
||||
import { getLinkParts } from "./Utils";
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
|
||||
import { MAX_IMAGE_SIZE } from "src/Constants";
|
||||
import { MAX_IMAGE_SIZE, IMAGE_TYPES } from "src/constants";
|
||||
import { TFile } from "obsidian";
|
||||
import { IMAGE_TYPES } from "src/Constants";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
|
||||
export const insertImageToView = async (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { DataURL } from "@zsviczian/excalidraw/types/types";
|
||||
import { loadPdfJs, normalizePath, Notice, requestUrl, RequestUrlResponse, TAbstractFile, TFile, TFolder, Vault } from "obsidian";
|
||||
import { URLFETCHTIMEOUT } from "src/Constants";
|
||||
import { URLFETCHTIMEOUT } from "src/constants";
|
||||
import { MimeType } from "src/EmbeddedFileLoader";
|
||||
import { ExcalidrawSettings } from "src/settings";
|
||||
import { errorlog, getDataURL } from "./Utils";
|
||||
|
||||
@@ -307,7 +307,7 @@ class ImageCache {
|
||||
const store = transaction.objectStore(this.cacheStoreName);
|
||||
const key = getKey(key_);
|
||||
store.put(data, key);
|
||||
if(Boolean(svg)) {
|
||||
if(!Boolean(svg)) {
|
||||
this.obsidanURLCache.set(key, obsidianURL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DEVICE, isDarwin } from "src/Constants";
|
||||
import { DEVICE, isDarwin } from "src/constants";
|
||||
export type ModifierKeys = {shiftKey:boolean, ctrlKey: boolean, metaKey: boolean, altKey: boolean};
|
||||
export type KeyEvent = PointerEvent | MouseEvent | KeyboardEvent | React.DragEvent | React.PointerEvent | React.MouseEvent | ModifierKeys;
|
||||
export type PaneTarget = "active-pane"|"new-pane"|"popout-window"|"new-tab"|"md-properties";
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { checkAndCreateFolder, splitFolderAndFilename } from "./FileUtils";
|
||||
import { linkClickModifierType, ModifierKeys } from "./ModifierkeyHelper";
|
||||
import { REG_BLOCK_REF_CLEAN, REG_SECTION_REF_CLEAN } from "src/Constants";
|
||||
import { REG_BLOCK_REF_CLEAN, REG_SECTION_REF_CLEAN } from "src/constants";
|
||||
|
||||
export const getParentOfClass = (element: Element, cssClass: string):HTMLElement | null => {
|
||||
let parent = element.parentElement;
|
||||
@@ -19,8 +19,6 @@ export const getParentOfClass = (element: Element, cssClass: string):HTMLElement
|
||||
return parent?.classList?.contains(cssClass) ? parent : null;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const getLeaf = (
|
||||
plugin: ExcalidrawPlugin,
|
||||
origo: WorkspaceLeaf,
|
||||
@@ -214,4 +212,24 @@ export const cleanBlockRef = (blockRef:string) => {
|
||||
export const legacyCleanBlockRef = (blockRef:string) => {
|
||||
if(!blockRef) return blockRef;
|
||||
return blockRef.replace(/[!"#$%&()*+,.:;<=>?@^`{|}~\/\[\]\\]/g, "").replace(/\s+/g, " ").trim();
|
||||
}
|
||||
}
|
||||
|
||||
export const getAllWindowDocuments = (app:App):Document[] => {
|
||||
const documents = new Set<Document>();
|
||||
documents.add(document);
|
||||
app.workspace.iterateAllLeaves(l=>{
|
||||
if(l.view.containerEl.ownerDocument !== document) {
|
||||
documents.add(l.view.containerEl.ownerDocument);
|
||||
}
|
||||
});
|
||||
return Array.from(documents);
|
||||
}
|
||||
|
||||
export const obsidianPDFQuoteWithRef = (text:string):{quote: string, link: string} => {
|
||||
const reg = /^> (.*)\n\n\[\[([^|\]]*)\|[^\]]*]]$/gm;
|
||||
const match = reg.exec(text);
|
||||
if(match) {
|
||||
return {quote: match[1], link: match[2]};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
127
src/utils/StylesManager.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { WorkspaceWindow } from "obsidian";
|
||||
import ExcalidrawPlugin from "src/main";
|
||||
import { getAllWindowDocuments } from "./ObsidianUtils";
|
||||
|
||||
const STYLE_VARIABLES = ["--background-modifier-cover","--background-primary-alt","--background-secondary","--background-secondary-alt","--background-modifier-border","--text-normal","--text-muted","--text-accent","--text-accent-hover","--text-faint","--text-highlight-bg","--text-highlight-bg-active","--text-selection","--interactive-normal","--interactive-hover","--interactive-accent","--interactive-accent-hover","--scrollbar-bg","--scrollbar-thumb-bg","--scrollbar-active-thumb-bg"];
|
||||
const EXCALIDRAW_CONTAINER_CLASS = "excalidraw__embeddable__outer";
|
||||
|
||||
export class StylesManager {
|
||||
private stylesMap = new Map<Document,{light: HTMLStyleElement, dark: HTMLStyleElement}>();
|
||||
private styleLight: string;
|
||||
private styleDark: string;
|
||||
private plugin: ExcalidrawPlugin;
|
||||
|
||||
constructor(plugin: ExcalidrawPlugin) {
|
||||
this.plugin = plugin;
|
||||
plugin.app.workspace.onLayoutReady(async () => {
|
||||
await this.harvestStyles();
|
||||
getAllWindowDocuments(plugin.app).forEach(doc => {
|
||||
this.copyPropertiesToTheme(doc);
|
||||
})
|
||||
|
||||
//initialize
|
||||
plugin.registerEvent(
|
||||
plugin.app.workspace.on("css-change", async () => {
|
||||
await this.harvestStyles();
|
||||
getAllWindowDocuments(plugin.app).forEach(doc => {
|
||||
this.copyPropertiesToTheme(doc);
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
plugin.registerEvent(
|
||||
plugin.app.workspace.on("window-open", (win: WorkspaceWindow, window: Window) => {
|
||||
this.stylesMap.set(win.doc, {
|
||||
light: document.head.querySelector(`style[id="excalidraw-embedded-light"]`),
|
||||
dark: document.head.querySelector(`style[id="excalidraw-embedded-dark"]`)
|
||||
});
|
||||
//this.copyPropertiesToTheme(win.doc);
|
||||
}),
|
||||
)
|
||||
|
||||
plugin.registerEvent(
|
||||
plugin.app.workspace.on("window-open", (win: WorkspaceWindow, window: Window) => {
|
||||
this.stylesMap.delete(win.doc);
|
||||
}),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
public unload() {
|
||||
for (const [doc, styleTags] of this.stylesMap) {
|
||||
doc.head.removeChild(styleTags.light);
|
||||
doc.head.removeChild(styleTags.dark);
|
||||
}
|
||||
}
|
||||
|
||||
private async harvestStyles() {
|
||||
const body = document.body;
|
||||
const iframe:HTMLIFrameElement = document.createElement("iframe");
|
||||
iframe.style.display = "none";
|
||||
body.appendChild(iframe);
|
||||
|
||||
const iframeLoadedPromise = new Promise<void>((resolve) => {
|
||||
iframe.addEventListener("load", () => resolve());
|
||||
});
|
||||
|
||||
const iframeDoc = iframe.contentWindow.document;
|
||||
const iframeWin = iframe.contentWindow;
|
||||
iframeDoc.open();
|
||||
iframeDoc.write(`<head>${document.head.innerHTML}</head>`);
|
||||
iframeDoc.close();
|
||||
|
||||
await iframeLoadedPromise;
|
||||
|
||||
const iframeBody = iframe.contentWindow.document.body;
|
||||
iframeBody.setAttribute("style", body.getAttribute("style"));
|
||||
iframeBody.setAttribute("class", body.getAttribute("class"));
|
||||
|
||||
const setTheme = (theme: "theme-light" | "theme-dark") => {
|
||||
iframeBody.classList.remove("theme-light");
|
||||
iframeBody.classList.remove("theme-dark");
|
||||
iframeBody.classList.add(theme);
|
||||
}
|
||||
|
||||
const getCSSVariables = (): string => {
|
||||
const computedStyles = iframeWin.getComputedStyle(iframeBody);
|
||||
const allVariables: {[key:string]:string} = {};
|
||||
for (const variable of STYLE_VARIABLES) {
|
||||
allVariables[variable] = computedStyles.getPropertyValue(variable);
|
||||
}
|
||||
const cm = this.plugin.ea.getCM(computedStyles.getPropertyValue("--background-primary"));
|
||||
cm.alphaTo(0.9);
|
||||
allVariables["--background-primary"] = cm.stringHEX();
|
||||
return Object.entries(allVariables)
|
||||
.map(([key, value]) => `${key}: ${value} !important;`)
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
setTheme("theme-light");
|
||||
this.styleLight = getCSSVariables();
|
||||
setTheme("theme-dark");
|
||||
this.styleDark = getCSSVariables();
|
||||
body.removeChild(iframe);
|
||||
}
|
||||
|
||||
private copyPropertiesToTheme(doc: Document) {
|
||||
const styleTags = this.stylesMap.get(doc);
|
||||
if (styleTags) {
|
||||
styleTags.light.innerHTML = `.${EXCALIDRAW_CONTAINER_CLASS} .theme-light {\n${this.styleLight}\n}`;
|
||||
styleTags.dark.innerHTML = `.${EXCALIDRAW_CONTAINER_CLASS} .theme-dark {\n${this.styleDark}\n}`;
|
||||
} else {
|
||||
const lightStyleTag = doc.createElement("style");
|
||||
lightStyleTag.type = "text/css";
|
||||
lightStyleTag.setAttribute("id", "excalidraw-embedded-light");
|
||||
lightStyleTag.innerHTML = `.${EXCALIDRAW_CONTAINER_CLASS} .theme-light {\n${this.styleLight}\n}`;
|
||||
doc.head.appendChild(lightStyleTag);
|
||||
|
||||
const darkStyleTag = doc.createElement("style");
|
||||
darkStyleTag.type = "text/css";
|
||||
darkStyleTag.setAttribute("id", "excalidraw-embedded-dark");
|
||||
darkStyleTag.innerHTML = `.${EXCALIDRAW_CONTAINER_CLASS} .theme-dark {\n${this.styleDark}\n}`;
|
||||
doc.head.appendChild(darkStyleTag);
|
||||
|
||||
this.stylesMap.set(doc, {light: lightStyleTag, dark: darkStyleTag});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,13 @@ import {
|
||||
FRONTMATTER_KEY_EXPORT_PADDING,
|
||||
exportToSvg,
|
||||
exportToBlob,
|
||||
} from "../Constants";
|
||||
IMAGE_TYPES
|
||||
} from "../constants";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { ExportSettings } from "../ExcalidrawView";
|
||||
import { compressToBase64, decompressFromBase64 } from "lz-string";
|
||||
import { getDataURLFromURL, getIMGFilename, getMimeType, getURLImageExtension } from "./FileUtils";
|
||||
import { IMAGE_TYPES } from "../Constants";
|
||||
import { generateEmbeddableLink } from "./CustomEmbeddableUtils";
|
||||
import ExcalidrawScene from "src/svgToExcalidraw/elements/ExcalidrawScene";
|
||||
import { FILENAMEPARTS } from "./UtilTypes";
|
||||
|
||||
33
styles.css
@@ -28,11 +28,13 @@
|
||||
.excalidraw-svg-right-wrap {
|
||||
float: right;
|
||||
margin: 0px 0px 20px 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.excalidraw-svg-left-wrap {
|
||||
float: left;
|
||||
margin: 0px 35px 20px 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.excalidraw-svg-right {
|
||||
@@ -42,6 +44,7 @@
|
||||
.excalidraw-svg-center {
|
||||
text-align: center;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.excalidraw-svg-left {
|
||||
@@ -381,4 +384,34 @@ div.excalidraw-draginfo {
|
||||
|
||||
.excalidraw-image-wrapper img {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.modal-content.excalidraw-scriptengine-install .search-bar-wrapper {
|
||||
position: sticky;
|
||||
top: 1em;
|
||||
margin-right: 1em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
flex-wrap: nowrap;
|
||||
z-index: 10;
|
||||
background: var(--background-secondary);
|
||||
padding: 0.5em;
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
float: right;
|
||||
max-width: 28em;
|
||||
}
|
||||
|
||||
.modal-content.excalidraw-scriptengine-install .hit-count {
|
||||
margin-left: 0.5em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.modal-content.excalidraw-scriptengine-install .active-highlight {
|
||||
border: 2px solid var(--color-accent-2);
|
||||
background-color: var(--color-accent);
|
||||
}
|
||||
|
||||
.excalidraw-svg svg a {
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"sourceMap": false,
|
||||
"sourceMap": true,
|
||||
"module": "es2015",
|
||||
"target": "es2017",
|
||||
"target": "es2017", //es2017 because script engine requires for async execution
|
||||
"allowJs": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"importHelpers": true,
|
||||
"resolveJsonModule": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"scripthost",
|
||||
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"sourceMap": true,
|
||||
"sourceMap": false,
|
||||
"module": "es2015",
|
||||
"target": "es2017", //script engine requires for async execution
|
||||
"target": "es2017", //es2017 because script engine requires for async execution
|
||||
"allowJs": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
"importHelpers": true,
|
||||
"resolveJsonModule": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"scripthost",
|
||||
|
||||