Compare commits

..

32 Commits

Author SHA1 Message Date
zsviczian
9c5b48c037 restructured onload 2024-12-12 11:47:03 +01:00
zsviczian
4406709920 fixed onceOffGPTVersionReset 2024-12-12 11:38:17 +01:00
zsviczian
b7ba0f8909 2.7.0-beta-1 2024-12-10 22:22:59 +01:00
zsviczian
c28911c739 Merge pull request #2144 from dmscode/master
Update zh-cn.ts to 9e1d491
2024-12-10 20:15:37 +01:00
dmscode
28088754ad Update zh-cn.ts to 9e1d491 2024-12-09 08:06:00 +08:00
zsviczian
9e1d491981 2.6.8 2024-12-08 16:09:00 +01:00
zsviczian
ab5caa4877 Merge pull request #2142 from TrillStones/master
[Script Contribution] [ Update] Image Occlusion
2024-12-08 15:56:47 +01:00
trillstones
44b580ae78 add image for image-occlusion script 2024-12-08 19:26:50 +08:00
trillstones
3859eddc80 add new image for image-occlusion script 2024-12-08 19:01:13 +08:00
trillstones
6098e1b42e add setting - Generate Images No Matter What
change card's and folder's naming logic
2024-12-08 17:29:00 +08:00
zsviczian
6ad8d2f620 2.6.8 - before field suggester implementation 2024-12-08 07:00:11 +01:00
zsviczian
5b3f3a56ad 2.6.8-beta-3, 0.17.6-17 2024-12-07 22:32:10 +01:00
zsviczian
f746b4f4ac Merge pull request #2140 from TrillStones/master
[Script Contribution] Image Occlusion
2024-12-07 21:29:55 +01:00
trillstones
3e4a3ace56 Update index-new.md for image occlusion script 2024-12-07 11:54:14 +08:00
trillstones
c72f6add40 Update index-new.md for image occlusion script 2024-12-07 11:45:21 +08:00
trillstones
6cfb125a38 Update index-new.md for image occlusion script 2024-12-07 11:44:27 +08:00
trillstones
c91e57e341 add image for Image-occlusion 2024-12-07 11:36:26 +08:00
trillstones
0ddd75e5fe Add Image Occlusion Script 2024-12-07 11:10:04 +08:00
zsviczian
382d4ca827 2.6.8-beta-2, Dynamic caret color based on text background 2024-12-01 16:32:26 +01:00
zsviczian
198e8f8cb7 2.6.8-beta-1 - settings loading is async, added detailed load timestamps ea.printStartupBreakdown(), delayed settings load 2024-12-01 11:28:05 +01:00
zsviczian
d3baa74ce7 Register ribbon icon during onLoad 2024-12-01 06:45:32 +01:00
zsviczian
995bfe962e 2.6.7, 0.17.6-14 2024-11-10 14:32:34 +01:00
zsviczian
59255fd954 2.6.6 2024-11-07 21:03:05 +01:00
zsviczian
1e9bed9192 Merge pull request #2101 from dmscode/master
Update zh-cn.ts to b0d3976
2024-11-05 07:42:13 +01:00
dmscode
a747a6f698 Update zh-cn.ts to b0d3976 2024-11-05 08:23:26 +08:00
zsviczian
b0d3976c27 2.6.5 2024-11-04 23:44:13 +01:00
zsviczian
7f77ab0743 2.6.5-beta-1 2024-11-04 19:11:32 +01:00
zsviczian
79da8afa0b Merge pull request #2099 from zsviczian/fix-textwrap-script-engine
fix script loading error
2024-11-04 13:30:19 +01:00
zsviczian
bb83523c0f fix script loading error 2024-11-04 12:29:20 +00:00
zsviczian
f83c0a8458 Merge pull request #2097 from dmscode/master
Update zh-cn.ts to 55ce645
2024-11-04 07:58:33 +01:00
dmscode
7411d51477 Update zh-cn.ts to 55ce645 2024-11-04 07:39:41 +08:00
zsviczian
55ce6456d8 2.6.4 2024-11-03 17:56:39 +01:00
33 changed files with 2458 additions and 738 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<!-- Blue star background -->
<path
d="M50 5 L61 40 L98 40 L68 62 L79 95 L50 75 L21 95 L32 62 L2 40 L39 40 Z"
fill="#4a9eff"
stroke="#1e1e1e"
stroke-width="2"
/>
<!-- White "A" text -->
<text
x="50"
y="65"
font-family="Arial"
font-size="40"
fill="white"
text-anchor="middle"
dominant-baseline="middle"
>A</text>
</svg>

After

Width:  |  Height:  |  Size: 517 B

View File

@@ -130,6 +130,7 @@ I would love to include your contribution in the script library. If you have a s
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Select%20Similar%20Elements.svg"/></div>|[[#Select Similar Elements]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Slideshow.svg"/></div>|[[#Slideshow]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Split%20Ellipse.svg"/></div>|[[#Split Ellipse]]|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Image%20Occlusion.svg"/></div>|[[#Image Occlusion]]|
## Collaboration and Export
**Keywords**: Sharing, Teamwork, Exporting, Distribution, Cooperative, Publish
@@ -154,6 +155,7 @@ I would love to include your contribution in the script library. If you have a s
|----|-----|
|<div><img src="https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Crop%20Vintage%20Mask.svg"/></div>|[[#Crop Vintage Mask]]|
---
# Description and Installation
@@ -267,6 +269,8 @@ 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/Crop%20Vintage%20Mask.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">Adds a rounded mask to the image by adding a full cover black mask and a rounded rectangle white mask. The script is also useful for adding just a black mask. In this case, run the script, then delete the white mask and add your custom white mask.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-crop-vintage.jpg'></td></tr></table>
## Custom Zoom
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Custom%20Zoom.md
@@ -395,6 +399,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/GPT-Draw-a-UI.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">This script was discontinued in favor of ExcaliAI. Draw a UI and let GPT create the code for you.<br><iframe width="400" height="225" src="https://www.youtube.com/embed/y3kHl_6Ll4w" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-draw-a-ui.jpg'></td></tr></table>
## Image Occlusion
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Image%20Occlusion.md
```
<table><tr valign='top'><td class="label">Author</td><td class="data"><a href='https://github.com/TrillStones'>@TrillStones</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/Image%20Occlusion.md'>File on GitHub</a></td></tr><tr valign='top'><td class="label">Description</td><td class="data">An Excalidraw script for creating Anki image occlusion cards in Obsidian, similar to Anki's Image Occlusion Enhanced add-on but integrated into your Obsidian workflow.<br><img src='https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/images/scripts-image-occlusion.png'></td></tr></table>
## Invert colors
```excalidraw-script-install
https://raw.githubusercontent.com/zsviczian/obsidian-excalidraw-plugin/master/ea-scripts/Invert%20colors.md

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

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

View File

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

View File

@@ -19,7 +19,7 @@
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.11.8",
"@zsviczian/excalidraw": "0.17.6-11",
"@zsviczian/excalidraw": "0.17.6-19",
"chroma-js": "^2.4.2",
"clsx": "^2.0.0",
"@zsviczian/colormaster": "^1.2.2",

View File

@@ -52,14 +52,15 @@ if (!isLib) console.log(manifest.version);
const packageString = isLib
? ""
: ';' + lzstring_pkg +
: ';const INITIAL_TIMESTAMP=Date.now();' + lzstring_pkg +
'\nlet REACT_PACKAGES = `' +
jsesc(react_pkg + reactdom_pkg, { quotes: 'backtick' }) +
'`;\n' +
'let EXCALIDRAW_PACKAGE = ""; const unpackExcalidraw = () => {EXCALIDRAW_PACKAGE = LZString.decompressFromBase64("' + LZString.compressToBase64(excalidraw_pkg) + '");};\n' +
'let {react, reactDOM } = window.eval.call(window, `(function() {' + '${REACT_PACKAGES};' + 'return {react: React, reactDOM: ReactDOM};})();`);\n' +
`let excalidrawLib = {};\n` +
'let PLUGIN_VERSION="' + manifest.version + '";';
'const PLUGIN_VERSION="' + manifest.version + '";';
const BASE_CONFIG = {
input: 'src/main.ts',

View File

@@ -6,13 +6,16 @@ import {
EditorSuggestTriggerInfo,
TFile,
} from "obsidian";
import { FRONTMATTER_KEYS_INFO } from "./SuggesterInfo";
import { FRONTMATTER_KEYS_INFO } from "../../dialogs/SuggesterInfo";
import {
EXCALIDRAW_AUTOMATE_INFO,
EXCALIDRAW_SCRIPTENGINE_INFO,
} from "./SuggesterInfo";
import type ExcalidrawPlugin from "../main";
} from "../../dialogs/SuggesterInfo";
import type ExcalidrawPlugin from "../../main";
/**
* The field suggester recommends document properties in source mode, ea and utils function and attribute names.
*/
export class FieldSuggester extends EditorSuggest<string> {
plugin: ExcalidrawPlugin;
suggestType: "ea" | "excalidraw" | "utils";

View File

@@ -0,0 +1,142 @@
import {
FuzzyMatch,
TFile,
CachedMetadata,
TextComponent,
App,
setIcon,
} from "obsidian";
import { SuggestionModal } from "./SuggestionModal";
import { t } from "src/lang/helpers";
import { LinkSuggestion } from "src/types/types";
import ExcalidrawPlugin from "src/main";
import { AUDIO_TYPES, CODE_TYPES, ICON_NAME, IMAGE_TYPES, VIDEO_TYPES } from "src/constants/constants";
export class FileSuggestionModal extends SuggestionModal<LinkSuggestion> {
text: TextComponent;
cache: CachedMetadata;
filesAndAliases: LinkSuggestion[];
file: TFile;
constructor(app: App, input: TextComponent, items: TFile[], private plugin: ExcalidrawPlugin) {
const filesAndAliases = [];
for (const file of items) {
const path = file.path;
filesAndAliases.push({ file, path, alias: "" });
const metadata = app.metadataCache.getFileCache(file); // Get metadata for the file
const aliases = metadata?.frontmatter?.aliases || []; // Check for frontmatter aliases
for (const alias of aliases) {
if(!alias) continue; // Skip empty aliases
filesAndAliases.push({ file, path, alias });
}
}
super(app, input.inputEl, filesAndAliases);
this.limit = 20;
this.filesAndAliases = filesAndAliases;
this.text = input;
this.suggestEl.style.maxWidth = "100%";
this.suggestEl.style.width = `${input.inputEl.clientWidth}px`;
this.inputEl.addEventListener("input", () => this.getFile());
this.setPlaceholder(t("SELECT_FILE_TO_INSERT"));
this.emptyStateText = t("NO_MATCH");
}
getFile() {
const v = this.inputEl.value;
const file = this.app.vault.getAbstractFileByPath(v);
if (file === this.file) {
return;
}
if (!(file instanceof TFile)) {
return;
}
this.file = file;
this.onInputChanged();
}
getSelectedItem() {
return this.file;
}
getItemText(item: LinkSuggestion) {
return `${item.file.path}${item.alias ? `|${item.alias}` : ""}`;
}
onChooseItem(item: LinkSuggestion) {
this.file = item.file;
this.text.setValue(this.getItemText(item));
this.text.onChanged();
}
selectSuggestion({ item }: FuzzyMatch<LinkSuggestion>) {
this.file = item.file;
this.text.setValue(this.getItemText(item));
this.onClose();
this.text.onChanged();
this.close();
}
renderSuggestion(result: FuzzyMatch<LinkSuggestion>, itemEl: HTMLElement) {
const { item, match: matches } = result || {};
itemEl.addClass("mod-complex");
const contentEl = itemEl.createDiv("suggestion-content");
const auxEl = itemEl.createDiv("suggestion-aux");
const titleEl = contentEl.createDiv("suggestion-title");
const noteEl = contentEl.createDiv("suggestion-note");
//el.style.flexDirection = "column";
//content.style.flexDirection = "initial";
if (!item) {
titleEl.setText(this.emptyStateText);
itemEl.addClass("is-selected");
return;
}
const path = item.file?.path ?? item.path;
const pathLength = path.length - item.file.name.length;
const matchElements = matches.matches.map((m) => {
return createSpan("suggestion-highlight");
});
const itemText = this.getItemText(item);
for (let i = pathLength; i < itemText.length; i++) {
const match = matches.matches.find((m) => m[0] === i);
if (match) {
const element = matchElements[matches.matches.indexOf(match)];
titleEl.appendChild(element);
element.appendText(itemText.substring(match[0], match[1]));
i += match[1] - match[0] - 1;
continue;
}
titleEl.appendText(itemText[i]);
}
noteEl.setText(path);
if(this.plugin.isExcalidrawFile(item.file)) {
setIcon(auxEl, ICON_NAME);
} else if (item.file.extension === "md") {
setIcon(auxEl, "square-pen");
} else if (IMAGE_TYPES.includes(item.file.extension)) {
setIcon(auxEl, "image");
} else if (VIDEO_TYPES.includes(item.file.extension)) {
setIcon(auxEl, "monitor-play");
} else if (AUDIO_TYPES.includes(item.file.extension)) {
setIcon(auxEl, "file-audio");
} else if (CODE_TYPES.includes(item.file.extension)) {
setIcon(auxEl, "file-code");
} else if (item.file.extension === "canvas") {
setIcon(auxEl, "layout-dashboard");
} else if (item.file.extension === "pdf") {
setIcon(auxEl, "book-open-text");
} else {
auxEl.setText(item.file.extension);
}
}
getItems() {
return this.filesAndAliases;
}
}

View File

@@ -0,0 +1,87 @@
import {
FuzzyMatch,
CachedMetadata,
TextComponent,
App,
TFolder,
} from "obsidian";
import { SuggestionModal } from "./SuggestionModal";
export class FolderSuggestionModal extends SuggestionModal<TFolder> {
text: TextComponent;
cache: CachedMetadata;
folders: TFolder[];
folder: TFolder;
constructor(app: App, input: TextComponent, items: TFolder[]) {
super(app, input.inputEl, items);
this.folders = [...items];
this.text = input;
this.inputEl.addEventListener("input", () => this.getFolder());
}
getFolder() {
const v = this.inputEl.value;
const folder = this.app.vault.getAbstractFileByPath(v);
if (folder == this.folder) {
return;
}
if (!(folder instanceof TFolder)) {
return;
}
this.folder = folder;
this.onInputChanged();
}
getItemText(item: TFolder) {
return item.path;
}
onChooseItem(item: TFolder) {
this.text.setValue(item.path);
this.folder = item;
}
selectSuggestion({ item }: FuzzyMatch<TFolder>) {
const link = item.path;
this.text.setValue(link);
this.onClose();
this.close();
}
renderSuggestion(result: FuzzyMatch<TFolder>, el: HTMLElement) {
const { item, match: matches } = result || {};
const content = el.createDiv({
cls: "suggestion-content",
});
if (!item) {
content.setText(this.emptyStateText);
content.parentElement.addClass("is-selected");
return;
}
const pathLength = item.path.length - item.name.length;
const matchElements = matches.matches.map((m) => {
return createSpan("suggestion-highlight");
});
for (let i = pathLength; i < item.path.length; i++) {
const match = matches.matches.find((m) => m[0] === i);
if (match) {
const element = matchElements[matches.matches.indexOf(match)];
content.appendChild(element);
element.appendText(item.path.substring(match[0], match[1]));
i += match[1] - match[0] - 1;
continue;
}
content.appendText(item.path[i]);
}
el.createDiv({
cls: "suggestion-note",
text: item.path,
});
}
getItems() {
return this.folders;
}
}

View File

@@ -0,0 +1,163 @@
import {
FuzzyMatch,
TFile,
BlockCache,
HeadingCache,
CachedMetadata,
TextComponent,
App,
} from "obsidian";
import { SuggestionModal } from "./SuggestionModal";
export class PathSuggestionModal extends SuggestionModal<
TFile | BlockCache | HeadingCache
> {
file: TFile;
files: TFile[];
text: TextComponent;
cache: CachedMetadata;
constructor(app: App, input: TextComponent, items: TFile[]) {
super(app, input.inputEl, items);
this.files = [...items];
this.text = input;
//this.getFile();
this.inputEl.addEventListener("input", this.getFile.bind(this));
}
getFile() {
const v = this.inputEl.value;
const file = this.app.metadataCache.getFirstLinkpathDest(
v.split(/[\^#]/).shift() || "",
"",
);
if (file == this.file) {
return;
}
this.file = file;
if (this.file) {
this.cache = this.app.metadataCache.getFileCache(this.file);
}
this.onInputChanged();
}
getItemText(item: TFile | HeadingCache | BlockCache) {
if (item instanceof TFile) {
return item.path;
}
if (Object.prototype.hasOwnProperty.call(item, "heading")) {
return (<HeadingCache>item).heading;
}
if (Object.prototype.hasOwnProperty.call(item, "id")) {
return (<BlockCache>item).id;
}
}
onChooseItem(item: TFile | HeadingCache | BlockCache) {
if (item instanceof TFile) {
this.text.setValue(item.basename);
this.file = item;
this.cache = this.app.metadataCache.getFileCache(this.file);
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
this.text.setValue(
`${this.file.basename}#${(<HeadingCache>item).heading}`,
);
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
this.text.setValue(`${this.file.basename}^${(<BlockCache>item).id}`);
}
}
selectSuggestion({ item }: FuzzyMatch<TFile | BlockCache | HeadingCache>) {
let link: string;
if (item instanceof TFile) {
link = item.basename;
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
link = `${this.file.basename}#${(<HeadingCache>item).heading}`;
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
link = `${this.file.basename}^${(<BlockCache>item).id}`;
}
this.text.setValue(link);
this.onClose();
this.close();
}
renderSuggestion(
result: FuzzyMatch<TFile | BlockCache | HeadingCache>,
el: HTMLElement,
) {
const { item, match: matches } = result || {};
const content = el.createDiv({
cls: "suggestion-content",
});
if (!item) {
content.setText(this.emptyStateText);
content.parentElement.addClass("is-selected");
return;
}
if (item instanceof TFile) {
const pathLength = item.path.length - item.name.length;
const matchElements = matches.matches.map((m) => {
return createSpan("suggestion-highlight");
});
for (
let i = pathLength;
i < item.path.length - item.extension.length - 1;
i++
) {
const match = matches.matches.find((m) => m[0] === i);
if (match) {
const element = matchElements[matches.matches.indexOf(match)];
content.appendChild(element);
element.appendText(item.path.substring(match[0], match[1]));
i += match[1] - match[0] - 1;
continue;
}
content.appendText(item.path[i]);
}
el.createDiv({
cls: "suggestion-note",
text: item.path,
});
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
content.setText((<HeadingCache>item).heading);
content.prepend(
createSpan({
cls: "suggestion-flair",
text: `H${(<HeadingCache>item).level}`,
}),
);
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
content.setText((<BlockCache>item).id);
}
}
get headings() {
if (!this.file) {
return [];
}
if (!this.cache) {
this.cache = this.app.metadataCache.getFileCache(this.file);
}
return this.cache.headings || [];
}
get blocks() {
if (!this.file) {
return [];
}
if (!this.cache) {
this.cache = this.app.metadataCache.getFileCache(this.file);
}
return Object.values(this.cache.blocks || {}) || [];
}
getItems() {
const v = this.inputEl.value;
if (/#/.test(v)) {
this.modifyInput = (i) => i.split(/#/).pop();
return this.headings;
} else if (/\^/.test(v)) {
this.modifyInput = (i) => i.split(/\^/).pop();
return this.blocks;
}
return this.files;
}
}

View File

@@ -0,0 +1,119 @@
import {
SuggestModal,
Scope,
} from "obsidian";
export class Suggester<T> {
owner: SuggestModal<T>;
items: T[];
suggestions: HTMLDivElement[];
selectedItem: number;
containerEl: HTMLElement;
constructor(owner: SuggestModal<T>, containerEl: HTMLElement, scope: Scope) {
this.containerEl = containerEl;
this.owner = owner;
containerEl.on(
"click",
".suggestion-item",
this.onSuggestionClick.bind(this),
);
containerEl.on(
"mousemove",
".suggestion-item",
this.onSuggestionMouseover.bind(this),
);
scope.register([], "ArrowUp", () => {
this.setSelectedItem(this.selectedItem - 1, true);
return false;
});
scope.register([], "ArrowDown", () => {
this.setSelectedItem(this.selectedItem + 1, true);
return false;
});
scope.register([], "Enter", (evt) => {
this.useSelectedItem(evt);
return false;
});
scope.register([], "Tab", (evt) => {
this.chooseSuggestion(evt);
return false;
});
}
chooseSuggestion(evt: KeyboardEvent) {
if (!this.items || !this.items.length) {
return;
}
const currentValue = this.items[this.selectedItem];
if (currentValue) {
this.owner.onChooseSuggestion(currentValue, evt);
}
}
onSuggestionClick(event: MouseEvent, el: HTMLDivElement): void {
event.preventDefault();
if (!this.suggestions || !this.suggestions.length) {
return;
}
const item = this.suggestions.indexOf(el);
this.setSelectedItem(item, false);
this.useSelectedItem(event);
}
onSuggestionMouseover(event: MouseEvent, el: HTMLDivElement): void {
if (!this.suggestions || !this.suggestions.length) {
return;
}
const item = this.suggestions.indexOf(el);
this.setSelectedItem(item, false);
}
empty() {
this.containerEl.empty();
}
setSuggestions(items: T[]) {
this.containerEl.empty();
const els: HTMLDivElement[] = [];
items.forEach((item) => {
const suggestionEl = this.containerEl.createDiv("suggestion-item");
this.owner.renderSuggestion(item, suggestionEl);
els.push(suggestionEl);
});
this.items = items;
this.suggestions = els;
this.setSelectedItem(0, false);
}
useSelectedItem(event: MouseEvent | KeyboardEvent) {
if (!this.items || !this.items.length) {
return;
}
const currentValue = this.items[this.selectedItem];
if (currentValue) {
this.owner.selectSuggestion(currentValue, event);
}
}
wrap(value: number, size: number): number {
return ((value % size) + size) % size;
}
setSelectedItem(index: number, scroll: boolean) {
const nIndex = this.wrap(index, this.suggestions.length);
const prev = this.suggestions[this.selectedItem];
const next = this.suggestions[nIndex];
if (prev) {
prev.removeClass("is-selected");
}
if (next) {
next.addClass("is-selected");
}
this.selectedItem = nIndex;
if (scroll) {
next.scrollIntoView(false);
}
}
}

View File

@@ -0,0 +1,128 @@
import {
FuzzyMatch,
App,
FuzzySuggestModal,
Scope,
} from "obsidian";
import { createPopper, Instance as PopperInstance } from "@popperjs/core";
import { Suggester } from "./Suggester";
export abstract class SuggestionModal<T> extends FuzzySuggestModal<T> {
items: T[] = [];
suggestions: HTMLDivElement[];
popper: WeakRef<PopperInstance>;
//@ts-ignore
scope: Scope = new Scope(this.app.scope);
suggester: Suggester<FuzzyMatch<T>>;
suggestEl: HTMLDivElement;
promptEl: HTMLDivElement;
emptyStateText: string = "No match found";
limit: number = 100;
shouldNotOpen: boolean;
constructor(app: App, inputEl: HTMLInputElement, items: T[]) {
super(app);
this.inputEl = inputEl;
this.items = items;
this.suggestEl = createDiv("suggestion-container");
this.contentEl = this.suggestEl.createDiv("suggestion");
this.suggester = new Suggester(this, this.contentEl, this.scope);
this.scope.register([], "Escape", this.onEscape.bind(this));
this.inputEl.addEventListener("input", this.onInputChanged.bind(this));
this.inputEl.addEventListener("focus", this.onFocus.bind(this));
this.inputEl.addEventListener("blur", this.close.bind(this));
this.suggestEl.on(
"mousedown",
".suggestion-container",
(event: MouseEvent) => {
event.preventDefault();
},
);
}
empty() {
this.suggester.empty();
}
onInputChanged(): void {
if (this.shouldNotOpen) {
return;
}
const inputStr = this.modifyInput(this.inputEl.value);
const suggestions = this.getSuggestions(inputStr);
if (suggestions.length > 0) {
this.suggester.setSuggestions(suggestions.slice(0, this.limit));
} else {
this.onNoSuggestion();
}
this.open();
}
onFocus(): void {
this.shouldNotOpen = false;
this.onInputChanged();
}
modifyInput(input: string): string {
return input;
}
onNoSuggestion() {
this.empty();
this.renderSuggestion(null, this.contentEl.createDiv("suggestion-item"));
}
open(): void {
// TODO: Figure out a better way to do this. Idea from Periodic Notes plugin
this.app.keymap.pushScope(this.scope);
this.inputEl.ownerDocument.body.appendChild(this.suggestEl);
this.popper = new WeakRef(createPopper(this.inputEl, this.suggestEl, {
placement: "bottom-start",
modifiers: [
{
name: "offset",
options: {
offset: [0, 10],
},
},
{
name: "flip",
options: {
fallbackPlacements: ["top"],
},
},
],
}));
}
onEscape(): void {
this.close();
this.shouldNotOpen = true;
}
close(): void {
// TODO: Figure out a better way to do this. Idea from Periodic Notes plugin
this.app.keymap.popScope(this.scope);
this.suggester.setSuggestions([]);
if (this.popper?.deref()) {
this.popper.deref().destroy();
}
this.inputEl.removeEventListener("input", this.onInputChanged.bind(this));
this.inputEl.removeEventListener("focus", this.onFocus.bind(this));
this.inputEl.removeEventListener("blur", this.close.bind(this));
this.suggestEl.detach();
}
createPrompt(prompts: HTMLSpanElement[]) {
if (!this.promptEl) {
this.promptEl = this.suggestEl.createDiv("prompt-instructions");
}
const prompt = this.promptEl.createDiv("prompt-instruction");
for (const p of prompts) {
prompt.appendChild(p);
}
}
abstract onChooseItem(item: T, evt: MouseEvent | KeyboardEvent): void;
abstract getItemText(arg: T): string;
abstract getItems(): T[];
}

View File

@@ -701,7 +701,11 @@ export class EmbeddedFilesLoader {
return;
}
const data = getMermaidText(element);
const result = await mermaidToExcalidraw(data, {fontSize: 20}, true);
const result = await mermaidToExcalidraw(
data,
{ themeVariables: { fontSize: "20" } },
true
);
if(!result) {
return;
}
@@ -767,7 +771,7 @@ export class EmbeddedFilesLoader {
}, 1200);
const iterator = loadIterator.bind(this)();
const concurency = 5;
const concurency = 3;
await new PromisePool(iterator, concurency).all();
clearInterval(addFilesTimer);

View File

@@ -17,7 +17,7 @@ import { MimeType } from "./EmbeddedFileLoader";
import { Editor, normalizePath, Notice, OpenViewState, RequestUrlResponse, TFile, TFolder, WorkspaceLeaf } from "obsidian";
import * as obsidian_module from "obsidian";
import ExcalidrawView, { ExportSettings, TextMode, getTextMode } from "src/ExcalidrawView";
import { ExcalidrawData, getMarkdownDrawingSection, REGEX_LINK } from "src/ExcalidrawData";
import { ExcalidrawData, getExcalidrawMarkdownHeaderSection, getMarkdownDrawingSection, REGEX_LINK } from "src/ExcalidrawData";
import {
FRONTMATTER,
nanoid,
@@ -133,6 +133,10 @@ export class ExcalidrawAutomate {
return DEVICE;
}
public printStartupBreakdown() {
this.plugin.printStarupBreakdown();
}
public help(target: Function | string) {
if (!target) {
log("Usage: ea.help(ea.functionName) or ea.help('propertyName') or ea.help('utils.functionName') - notice property name and utils function name is in quotes");
@@ -650,6 +654,13 @@ export class ExcalidrawAutomate {
0
)
: null;
if (template?.plaintext) {
if(params.plaintext) {
params.plaintext = params.plaintext + "\n\n" + template.plaintext;
} else {
params.plaintext = template.plaintext;
}
}
let elements = template ? template.elements : [];
elements = elements.concat(this.getElements());
let frontmatter: string;
@@ -675,7 +686,13 @@ export class ExcalidrawAutomate {
: FRONTMATTER;
}
frontmatter += params.plaintext ? params.plaintext + "\n\n" : "";
frontmatter += params.plaintext
? (params.plaintext.endsWith("\n\n")
? params.plaintext
: (params.plaintext.endsWith("\n")
? params.plaintext + "\n"
: params.plaintext + "\n\n"))
: "";
if(template?.frontmatter && params?.frontmatterKeys) {
//the frontmatter tags supplyed to create take priority
frontmatter = mergeMarkdownFiles(template.frontmatter,frontmatter);
@@ -1472,7 +1489,12 @@ export class ExcalidrawAutomate {
diagram: string,
groupElements: boolean = true,
): Promise<string[]|string> {
const result = await mermaidToExcalidraw(diagram, {fontSize: this.style.fontSize});
const result = await mermaidToExcalidraw(
diagram, {
themeVariables: {fontSize: `${this.style.fontSize}`},
flowchart: {curve: this.style.roundness===null ? "linear" : "basis"},
}
);
const ids:string[] = [];
if(!result) return null;
if(result?.error) return result.error;
@@ -1820,7 +1842,7 @@ export class ExcalidrawAutomate {
viewBackgroundColor: "#FFFFFF",
gridSize: 0
};
};
};
/**
* returns true if MD file is an Excalidraw file
@@ -2854,10 +2876,9 @@ export class ExcalidrawAutomate {
}
};
export async function initExcalidrawAutomate(
export function initExcalidrawAutomate(
plugin: ExcalidrawPlugin,
): Promise<ExcalidrawAutomate> {
await initFonts();
): ExcalidrawAutomate {
const ea = new ExcalidrawAutomate(plugin);
//@ts-ignore
window.ExcalidrawAutomate = ea;
@@ -2892,14 +2913,6 @@ function getFontFamily(id: number):string {
return getFontFamilyString({fontFamily:id})
}
export async function initFonts():Promise<void> {
/*await excalidrawLib.registerFontsInCSS();
const fonts = excalidrawLib.getFontFamilies();
for(let i=0;i<fonts.length;i++) {
if(fonts[i] !== "Local Font") await (document as any).fonts.load(`16px ${fonts[i]}`);
};*/
}
export function _measureText(
newText: string,
fontSize: number,
@@ -2935,6 +2948,7 @@ async function getTemplate(
frontmatter: string;
files: any;
hasSVGwithBitmap: boolean;
plaintext: string; //markdown data above Excalidraw data and below YAML frontmatter
}> {
const app = plugin.app;
const vault = app.vault;
@@ -2960,6 +2974,7 @@ async function getTemplate(
frontmatter: "",
files: excalidrawData.scene.files,
hasSVGwithBitmap,
plaintext: "",
};
}
@@ -3032,7 +3047,7 @@ async function getTemplate(
}
excalidrawData.destroy();
const filehead = data.substring(0, trimLocation);
const filehead = getExcalidrawMarkdownHeaderSection(data); // data.substring(0, trimLocation);
let files:any = {};
const sceneFilesSize = Object.values(scene.files).length;
if (sceneFilesSize > 0) {
@@ -3045,6 +3060,7 @@ async function getTemplate(
}
}
const frontmatter = filehead.match(/^---\n.*\n---\n/ms)?.[0] ?? filehead;
return {
elements: convertMarkdownLinksToObsidianURLs
? updateElementLinksToObsidianLinks({
@@ -3052,7 +3068,10 @@ async function getTemplate(
hostFile: file,
}) : groupElements,
appState: scene.appState,
frontmatter: filehead.match(/^---\n.*\n---\n/ms)?.[0] ?? filehead,
frontmatter,
plaintext: frontmatter !== filehead
? (filehead.split(/^---\n.*\n---\n/ms)?.[1] ?? "")
: "",
files,
hasSVGwithBitmap,
};
@@ -3063,6 +3082,7 @@ async function getTemplate(
frontmatter: null,
files: [],
hasSVGwithBitmap,
plaintext: "",
};
}

View File

@@ -756,7 +756,7 @@ export class ExcalidrawData {
displayFontMessage(this.app);
}
},5000);
await loadSceneFonts(this.scene.elements);
const fontFaces = await loadSceneFonts(this.scene.elements);
clearTimeout(timer);
if (!this.scene.files) {
@@ -1588,6 +1588,7 @@ export class ExcalidrawData {
.filter(el=>el.type === "image" && el.crop && !el.isDeleted)
.forEach((el: Mutable<ExcalidrawImageElement>)=>{
const ef = this.getFile(el.fileId);
if(!ef.file) return;
if(ef.file.extension !== "pdf") return;
const pageRef = ef.linkParts.original.split("#")?.[1];
if(!pageRef || !pageRef.startsWith("page=") || pageRef.includes("rect")) return;

View File

@@ -6,7 +6,38 @@ import { FontMetadata } from "@zsviczian/excalidraw/types/excalidraw/fonts/metad
import { AppState, BinaryFiles, DataURL, GenerateDiagramToCode, Zoom } from "@zsviczian/excalidraw/types/excalidraw/types";
import { Mutable } from "@zsviczian/excalidraw/types/excalidraw/utility-types";
import { GlobalPoint } from "@zsviczian/excalidraw/types/math/types";
import ExcalidrawPlugin from "./main";
interface MermaidConfig {
/**
* Whether to start the diagram automatically when the page loads.
* @default false
*/
startOnLoad?: boolean;
/**
* The flowchart curve style.
* @default "linear"
*/
flowchart?: {
curve?: "linear" | "basis";
};
/**
* Theme variables
* @default { fontSize: "25px" }
*/
themeVariables?: {
fontSize?: string;
};
/**
* Maximum number of edges to be rendered.
* @default 1000
*/
maxEdges?: number;
/**
* Maximum number of characters to be rendered.
* @default 1000
*/
maxTextSize?: number;
}
type EmbeddedLink =
| ({
@@ -159,7 +190,7 @@ declare namespace ExcalidrawLib {
function mermaidToExcalidraw(
mermaidDefinition: string,
opts: {fontSize: number},
opts: MermaidConfig,
forceSVG?: boolean,
): Promise<{
elements?: ExcalidrawElement[];

View File

@@ -295,7 +295,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
private lastLoadedFile: TFile = null;
//store key state for view mode link resolution
private modifierKeyDown: ModifierKeys = {shiftKey:false, metaKey: false, ctrlKey: false, altKey: false}
public currentPosition: {x:number,y:number} = { x: 0, y: 0 };
public currentPosition: {x:number,y:number} = { x: 0, y: 0 }; //these are scene coord thus would be more apt to call them sceneX and sceneY, however due to scrits already using x and y, I will keep it as is
//Obsidian 0.15.0
private draginfoDiv: HTMLDivElement;
public canvasNodeFactory: CanvasNodeFactory;
@@ -1527,6 +1527,10 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
onload() {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.onload, "ExcalidrawView.onload");
if(this.plugin.settings.overrideObsidianFontSize) {
document.documentElement.style.fontSize = "";
}
const apiMissing = Boolean(typeof this.containerEl.onWindowMigrated === "undefined")
this.packages = this.plugin.getPackage(this.ownerWindow);
@@ -3263,6 +3267,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
markdownlink: string,
path: string,
alias: string,
originalLink?: string,
) {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.addLink, "ExcalidrawView.addLink", markdownlink, path, alias);
const api = this.excalidrawAPI as ExcalidrawImperativeAPI;
@@ -3276,16 +3281,28 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
}
const selectedElementId = Object.keys(api.getAppState().selectedElementIds)[0];
const selectedElement = api.getSceneElements().find(el=>el.id === selectedElementId);
if(!selectedElement || (selectedElement && selectedElement.link !== null)) {
if(!selectedElement || (!Boolean(originalLink) && (selectedElement && selectedElement.link !== null) )) {
if(selectedElement) new Notice("Selected element already has a link. Inserting link as text.");
this.addText(markdownlink);
return;
}
const ea = getEA(this) as ExcalidrawAutomate;
ea.copyViewElementsToEAforEditing([selectedElement]);
if(originalLink?.match(/\[\[(.*?)\]\]/)?.[1]) {
markdownlink = originalLink.replace(/(\[\[.*?\]\])/,markdownlink);
}
ea.getElement(selectedElementId).link = markdownlink;
await ea.addElementsToView(false, true);
ea.destroy();
if(Boolean(originalLink)) {
this.updateScene({
appState: {
showHyperlinkPopup: {
newValue : "info", oldValue : "editor"
}
}
});
}
}
public async addText (
@@ -3542,7 +3559,10 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
}
private clearHoverPreview() {
if (this.hoverPopover) {
//@ts-ignore
const hoverContainerEl = this.hoverPopover?.containerEl;
//don't auto hide hover-editor
if (this.hoverPopover && !hoverContainerEl?.parentElement?.hasClass("hover-editor")) {
this.hoverPreviewTarget = null;
//@ts-ignore
if(this.hoverPopover.embed?.editor) {
@@ -4653,8 +4673,20 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
//returns the raw text of the element which is the original text without parsing
//in compatibility mode, returns the original text, and for backward compatibility the text if originalText is not available
private onBeforeTextEdit (textElement: ExcalidrawTextElement) {
private onBeforeTextEdit (textElement: ExcalidrawTextElement, isExistingElement: boolean): string {
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.onBeforeTextEdit, "ExcalidrawView.onBeforeTextEdit", textElement);
/*const api = this.excalidrawAPI as ExcalidrawImperativeAPI;
const st = api.getAppState();
setDynamicStyle(
this.plugin.ea,
this,
st.viewBackgroundColor === "transparent" ? "white" : st.viewBackgroundColor,
this.plugin.settings.dynamicStyling,
api.getColorAtScenePoint({sceneX: this.currentPosition.x, sceneY: this.currentPosition.y})
);*/
if(!isExistingElement) {
return;
}
window.clearTimeout(this.isEditingTextResetTimer);
this.isEditingTextResetTimer = null;
this.semaphores.isEditingText = true; //to prevent autoresize on mobile when keyboard pops up
@@ -5075,6 +5107,15 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
new Notice("Image successfully converted to local file");
}
private insertLinkAction(linkVal: string) {
let link = linkVal.match(/\[\[(.*?)\]\]/)?.[1];
if(!link) {
link = linkVal.replaceAll("[","").replaceAll("]","");
link = link.split("|")[0].trim();
}
this.plugin.insertLinkDialog.start(this.file.path, (markdownlink: string, path:string, alias:string) => this.addLink(markdownlink, path, alias, linkVal), link);
}
private onContextMenu(elements: readonly ExcalidrawElement[], appState: AppState, onClose: (callback?: () => void) => void) {
const React = this.packages.react;
const contextMenuActions = [];
@@ -5897,6 +5938,7 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
renderEmbeddable: this.renderEmbeddable.bind(this),
renderMermaid: shouldRenderMermaid,
showDeprecatedFonts: true,
insertLinkAction: this.insertLinkAction.bind(this),
},
this.renderCustomActionsMenu(),
this.renderWelcomeScreen(),

View File

@@ -886,6 +886,7 @@ export const markdownPostProcessor = async (
el: HTMLElement,
ctx: MarkdownPostProcessorContext,
) => {
await plugin.awaitSettings();
const isPrinting = Boolean(document.body.querySelectorAll("body > .print").length>0);
//firstElementChild: https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1956
const isFrontmatter = el.hasClass("mod-frontmatter") ||

View File

@@ -1,6 +1,7 @@
import {
App,
Instruction,
normalizePath,
TAbstractFile,
TFile,
WorkspaceLeaf,
@@ -22,6 +23,7 @@ export type ScriptIconMap = {
export class ScriptEngine {
private plugin: ExcalidrawPlugin;
private app: App;
private scriptPath: string;
//https://stackoverflow.com/questions/60218638/how-to-force-re-render-if-map-value-changes
public scriptIconMap: ScriptIconMap;
@@ -29,6 +31,7 @@ export class ScriptEngine {
constructor(plugin: ExcalidrawPlugin) {
this.plugin = plugin;
this.app = plugin.app;
this.scriptIconMap = {};
this.loadScripts();
this.registerEventHandlers();
@@ -58,7 +61,7 @@ export class ScriptEngine {
if (!path.endsWith(".svg")) {
return;
}
const scriptFile = app.vault.getAbstractFileByPath(
const scriptFile = this.app.vault.getAbstractFileByPath(
getIMGFilename(path, "md"),
);
if (scriptFile && scriptFile instanceof TFile) {
@@ -107,19 +110,19 @@ export class ScriptEngine {
registerEventHandlers() {
this.plugin.registerEvent(
this.plugin.app.vault.on(
this.app.vault.on(
"delete",
(file: TFile)=>this.deleteEventHandler(file)
),
);
this.plugin.registerEvent(
this.plugin.app.vault.on(
this.app.vault.on(
"create",
(file: TFile)=>this.createEventHandler(file)
),
);
this.plugin.registerEvent(
this.plugin.app.vault.on(
this.app.vault.on(
"rename",
(file: TAbstractFile, oldPath: string)=>this.renameEventHandler(file, oldPath)
),
@@ -138,15 +141,16 @@ export class ScriptEngine {
public getListofScripts(): TFile[] {
this.scriptPath = this.plugin.settings.scriptFolderPath;
if (!app.vault.getAbstractFileByPath(this.scriptPath)) {
//this.scriptPath = null;
if(!this.scriptPath) return;
this.scriptPath = normalizePath(this.scriptPath);
if (!this.app.vault.getAbstractFileByPath(this.scriptPath)) {
return;
}
return app.vault
return this.app.vault
.getFiles()
.filter(
(f: TFile) =>
f.path.startsWith(this.scriptPath) && f.extension === "md",
f.path.startsWith(this.scriptPath+"/") && f.extension === "md",
);
}
@@ -166,7 +170,10 @@ export class ScriptEngine {
}
const subpath = path.split(`${this.scriptPath}/`)[1];
const lastSlash = subpath.lastIndexOf("/");
if(!subpath) {
console.warn(`ScriptEngine.getScriptName unexpected basename: ${basename}; path: ${path}`)
}
const lastSlash = subpath?.lastIndexOf("/");
if (lastSlash > -1) {
return subpath.substring(0, lastSlash + 1) + basename;
}
@@ -175,10 +182,10 @@ export class ScriptEngine {
async addScriptIconToMap(scriptPath: string, name: string) {
const svgFilePath = getIMGFilename(scriptPath, "svg");
const file = app.vault.getAbstractFileByPath(svgFilePath);
const file = this.app.vault.getAbstractFileByPath(svgFilePath);
const svgString: string =
file && file instanceof TFile
? await app.vault.read(file)
? await this.app.vault.read(file)
: null;
this.scriptIconMap = {
...this.scriptIconMap,
@@ -199,12 +206,12 @@ export class ScriptEngine {
name: `(Script) ${scriptName}`,
checkCallback: (checking: boolean) => {
if (checking) {
return Boolean(app.workspace.getActiveViewOfType(ExcalidrawView));
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView));
}
const view = app.workspace.getActiveViewOfType(ExcalidrawView);
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
if (view) {
(async()=>{
const script = await app.vault.read(f);
const script = await this.app.vault.read(f);
if(script) {
//remove YAML frontmatter if present
this.executeScript(view, script, scriptName,f);
@@ -218,7 +225,7 @@ export class ScriptEngine {
}
unloadScripts() {
const scripts = app.vault
const scripts = this.app.vault
.getFiles()
.filter((f: TFile) => f.path.startsWith(this.scriptPath));
scripts.forEach((f) => {
@@ -236,11 +243,11 @@ export class ScriptEngine {
const commandId = `${PLUGIN_ID}:${basename}`;
// @ts-ignore
if (!this.plugin.app.commands.commands[commandId]) {
if (!this.app.commands.commands[commandId]) {
return;
}
// @ts-ignore
delete this.plugin.app.commands.commands[commandId];
delete this.app.commands.commands[commandId];
}
async executeScript(view: ExcalidrawView, script: string, title: string, file: TFile) {
@@ -271,7 +278,7 @@ export class ScriptEngine {
ScriptEngine.inputPrompt(
view,
this.plugin,
this.plugin.app,
this.app,
header,
placeholder,
value,
@@ -288,7 +295,7 @@ export class ScriptEngine {
instructions?: Instruction[],
) =>
ScriptEngine.suggester(
this.plugin.app,
this.app,
displayItems,
items,
hint,
@@ -304,7 +311,7 @@ export class ScriptEngine {
}
private updateToolPannels() {
const excalidrawViews = getExcalidrawViews(this.plugin.app);
const excalidrawViews = getExcalidrawViews(this.app);
excalidrawViews.forEach(excalidrawView => {
excalidrawView.toolsPanelRef?.current?.updateScriptIconMap(
this.scriptIconMap,

View File

@@ -195,6 +195,10 @@ export const ANIMATED_IMAGE_TYPES = ["gif", "webp", "apng", "svg"];
export const EXPORT_TYPES = ["svg", "dark.svg", "light.svg", "png", "dark.png", "light.png"];
export const MAX_IMAGE_SIZE = 500;
export const VIDEO_TYPES = ["mp4", "webm", "ogv", "mov", "mkv"];
export const AUDIO_TYPES = ["mp3", "wav", "m4a", "3gp", "flac", "ogg", "oga", "opus"];
export const CODE_TYPES = ["json", "css", "js"];
export const FRONTMATTER_KEYS:{[key:string]: {name: string, type: string, depricated?:boolean}} = {
"plugin": {name: "excalidraw-plugin", type: "text"},
"export-transparent": {name: "excalidraw-export-transparent", type: "checkbox"},

View File

@@ -1,569 +0,0 @@
import {
FuzzyMatch,
TFile,
BlockCache,
HeadingCache,
CachedMetadata,
TextComponent,
App,
TFolder,
FuzzySuggestModal,
SuggestModal,
Scope,
} from "obsidian";
import { createPopper, Instance as PopperInstance } from "@popperjs/core";
class Suggester<T> {
owner: SuggestModal<T>;
items: T[];
suggestions: HTMLDivElement[];
selectedItem: number;
containerEl: HTMLElement;
constructor(owner: SuggestModal<T>, containerEl: HTMLElement, scope: Scope) {
this.containerEl = containerEl;
this.owner = owner;
containerEl.on(
"click",
".suggestion-item",
this.onSuggestionClick.bind(this),
);
containerEl.on(
"mousemove",
".suggestion-item",
this.onSuggestionMouseover.bind(this),
);
scope.register([], "ArrowUp", () => {
this.setSelectedItem(this.selectedItem - 1, true);
return false;
});
scope.register([], "ArrowDown", () => {
this.setSelectedItem(this.selectedItem + 1, true);
return false;
});
scope.register([], "Enter", (evt) => {
this.useSelectedItem(evt);
return false;
});
scope.register([], "Tab", (evt) => {
this.chooseSuggestion(evt);
return false;
});
}
chooseSuggestion(evt: KeyboardEvent) {
if (!this.items || !this.items.length) {
return;
}
const currentValue = this.items[this.selectedItem];
if (currentValue) {
this.owner.onChooseSuggestion(currentValue, evt);
}
}
onSuggestionClick(event: MouseEvent, el: HTMLDivElement): void {
event.preventDefault();
if (!this.suggestions || !this.suggestions.length) {
return;
}
const item = this.suggestions.indexOf(el);
this.setSelectedItem(item, false);
this.useSelectedItem(event);
}
onSuggestionMouseover(event: MouseEvent, el: HTMLDivElement): void {
if (!this.suggestions || !this.suggestions.length) {
return;
}
const item = this.suggestions.indexOf(el);
this.setSelectedItem(item, false);
}
empty() {
this.containerEl.empty();
}
setSuggestions(items: T[]) {
this.containerEl.empty();
const els: HTMLDivElement[] = [];
items.forEach((item) => {
const suggestionEl = this.containerEl.createDiv("suggestion-item");
this.owner.renderSuggestion(item, suggestionEl);
els.push(suggestionEl);
});
this.items = items;
this.suggestions = els;
this.setSelectedItem(0, false);
}
useSelectedItem(event: MouseEvent | KeyboardEvent) {
if (!this.items || !this.items.length) {
return;
}
const currentValue = this.items[this.selectedItem];
if (currentValue) {
this.owner.selectSuggestion(currentValue, event);
}
}
wrap(value: number, size: number): number {
return ((value % size) + size) % size;
}
setSelectedItem(index: number, scroll: boolean) {
const nIndex = this.wrap(index, this.suggestions.length);
const prev = this.suggestions[this.selectedItem];
const next = this.suggestions[nIndex];
if (prev) {
prev.removeClass("is-selected");
}
if (next) {
next.addClass("is-selected");
}
this.selectedItem = nIndex;
if (scroll) {
next.scrollIntoView(false);
}
}
}
export abstract class SuggestionModal<T> extends FuzzySuggestModal<T> {
items: T[] = [];
suggestions: HTMLDivElement[];
popper: WeakRef<PopperInstance>;
//@ts-ignore
scope: Scope = new Scope(this.app.scope);
suggester: Suggester<FuzzyMatch<T>>;
suggestEl: HTMLDivElement;
promptEl: HTMLDivElement;
emptyStateText: string = "No match found";
limit: number = 100;
shouldNotOpen: boolean;
constructor(app: App, inputEl: HTMLInputElement, items: T[]) {
super(app);
this.inputEl = inputEl;
this.items = items;
this.suggestEl = createDiv("suggestion-container");
this.contentEl = this.suggestEl.createDiv("suggestion");
this.suggester = new Suggester(this, this.contentEl, this.scope);
this.scope.register([], "Escape", this.onEscape.bind(this));
this.inputEl.addEventListener("input", this.onInputChanged.bind(this));
this.inputEl.addEventListener("focus", this.onFocus.bind(this));
this.inputEl.addEventListener("blur", this.close.bind(this));
this.suggestEl.on(
"mousedown",
".suggestion-container",
(event: MouseEvent) => {
event.preventDefault();
},
);
}
empty() {
this.suggester.empty();
}
onInputChanged(): void {
if (this.shouldNotOpen) {
return;
}
const inputStr = this.modifyInput(this.inputEl.value);
const suggestions = this.getSuggestions(inputStr);
if (suggestions.length > 0) {
this.suggester.setSuggestions(suggestions.slice(0, this.limit));
} else {
this.onNoSuggestion();
}
this.open();
}
onFocus(): void {
this.shouldNotOpen = false;
this.onInputChanged();
}
modifyInput(input: string): string {
return input;
}
onNoSuggestion() {
this.empty();
this.renderSuggestion(null, this.contentEl.createDiv("suggestion-item"));
}
open(): void {
// TODO: Figure out a better way to do this. Idea from Periodic Notes plugin
this.app.keymap.pushScope(this.scope);
this.inputEl.ownerDocument.body.appendChild(this.suggestEl);
this.popper = new WeakRef(createPopper(this.inputEl, this.suggestEl, {
placement: "bottom-start",
modifiers: [
{
name: "offset",
options: {
offset: [0, 10],
},
},
{
name: "flip",
options: {
fallbackPlacements: ["top"],
},
},
],
}));
}
onEscape(): void {
this.close();
this.shouldNotOpen = true;
}
close(): void {
// TODO: Figure out a better way to do this. Idea from Periodic Notes plugin
this.app.keymap.popScope(this.scope);
this.suggester.setSuggestions([]);
if (this.popper?.deref()) {
this.popper.deref().destroy();
}
this.inputEl.removeEventListener("input", this.onInputChanged.bind(this));
this.inputEl.removeEventListener("focus", this.onFocus.bind(this));
this.inputEl.removeEventListener("blur", this.close.bind(this));
this.suggestEl.detach();
}
createPrompt(prompts: HTMLSpanElement[]) {
if (!this.promptEl) {
this.promptEl = this.suggestEl.createDiv("prompt-instructions");
}
const prompt = this.promptEl.createDiv("prompt-instruction");
for (const p of prompts) {
prompt.appendChild(p);
}
}
abstract onChooseItem(item: T, evt: MouseEvent | KeyboardEvent): void;
abstract getItemText(arg: T): string;
abstract getItems(): T[];
}
export class PathSuggestionModal extends SuggestionModal<
TFile | BlockCache | HeadingCache
> {
file: TFile;
files: TFile[];
text: TextComponent;
cache: CachedMetadata;
constructor(app: App, input: TextComponent, items: TFile[]) {
super(app, input.inputEl, items);
this.files = [...items];
this.text = input;
//this.getFile();
this.inputEl.addEventListener("input", this.getFile.bind(this));
}
getFile() {
const v = this.inputEl.value;
const file = this.app.metadataCache.getFirstLinkpathDest(
v.split(/[\^#]/).shift() || "",
"",
);
if (file == this.file) {
return;
}
this.file = file;
if (this.file) {
this.cache = this.app.metadataCache.getFileCache(this.file);
}
this.onInputChanged();
}
getItemText(item: TFile | HeadingCache | BlockCache) {
if (item instanceof TFile) {
return item.path;
}
if (Object.prototype.hasOwnProperty.call(item, "heading")) {
return (<HeadingCache>item).heading;
}
if (Object.prototype.hasOwnProperty.call(item, "id")) {
return (<BlockCache>item).id;
}
}
onChooseItem(item: TFile | HeadingCache | BlockCache) {
if (item instanceof TFile) {
this.text.setValue(item.basename);
this.file = item;
this.cache = this.app.metadataCache.getFileCache(this.file);
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
this.text.setValue(
`${this.file.basename}#${(<HeadingCache>item).heading}`,
);
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
this.text.setValue(`${this.file.basename}^${(<BlockCache>item).id}`);
}
}
selectSuggestion({ item }: FuzzyMatch<TFile | BlockCache | HeadingCache>) {
let link: string;
if (item instanceof TFile) {
link = item.basename;
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
link = `${this.file.basename}#${(<HeadingCache>item).heading}`;
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
link = `${this.file.basename}^${(<BlockCache>item).id}`;
}
this.text.setValue(link);
this.onClose();
this.close();
}
renderSuggestion(
result: FuzzyMatch<TFile | BlockCache | HeadingCache>,
el: HTMLElement,
) {
const { item, match: matches } = result || {};
const content = el.createDiv({
cls: "suggestion-content",
});
if (!item) {
content.setText(this.emptyStateText);
content.parentElement.addClass("is-selected");
return;
}
if (item instanceof TFile) {
const pathLength = item.path.length - item.name.length;
const matchElements = matches.matches.map((m) => {
return createSpan("suggestion-highlight");
});
for (
let i = pathLength;
i < item.path.length - item.extension.length - 1;
i++
) {
const match = matches.matches.find((m) => m[0] === i);
if (match) {
const element = matchElements[matches.matches.indexOf(match)];
content.appendChild(element);
element.appendText(item.path.substring(match[0], match[1]));
i += match[1] - match[0] - 1;
continue;
}
content.appendText(item.path[i]);
}
el.createDiv({
cls: "suggestion-note",
text: item.path,
});
} else if (Object.prototype.hasOwnProperty.call(item, "heading")) {
content.setText((<HeadingCache>item).heading);
content.prepend(
createSpan({
cls: "suggestion-flair",
text: `H${(<HeadingCache>item).level}`,
}),
);
} else if (Object.prototype.hasOwnProperty.call(item, "id")) {
content.setText((<BlockCache>item).id);
}
}
get headings() {
if (!this.file) {
return [];
}
if (!this.cache) {
this.cache = this.app.metadataCache.getFileCache(this.file);
}
return this.cache.headings || [];
}
get blocks() {
if (!this.file) {
return [];
}
if (!this.cache) {
this.cache = this.app.metadataCache.getFileCache(this.file);
}
return Object.values(this.cache.blocks || {}) || [];
}
getItems() {
const v = this.inputEl.value;
if (/#/.test(v)) {
this.modifyInput = (i) => i.split(/#/).pop();
return this.headings;
} else if (/\^/.test(v)) {
this.modifyInput = (i) => i.split(/\^/).pop();
return this.blocks;
}
return this.files;
}
}
export class FolderSuggestionModal extends SuggestionModal<TFolder> {
text: TextComponent;
cache: CachedMetadata;
folders: TFolder[];
folder: TFolder;
constructor(app: App, input: TextComponent, items: TFolder[]) {
super(app, input.inputEl, items);
this.folders = [...items];
this.text = input;
this.inputEl.addEventListener("input", () => this.getFolder());
}
getFolder() {
const v = this.inputEl.value;
const folder = this.app.vault.getAbstractFileByPath(v);
if (folder == this.folder) {
return;
}
if (!(folder instanceof TFolder)) {
return;
}
this.folder = folder;
this.onInputChanged();
}
getItemText(item: TFolder) {
return item.path;
}
onChooseItem(item: TFolder) {
this.text.setValue(item.path);
this.folder = item;
}
selectSuggestion({ item }: FuzzyMatch<TFolder>) {
const link = item.path;
this.text.setValue(link);
this.onClose();
this.close();
}
renderSuggestion(result: FuzzyMatch<TFolder>, el: HTMLElement) {
const { item, match: matches } = result || {};
const content = el.createDiv({
cls: "suggestion-content",
});
if (!item) {
content.setText(this.emptyStateText);
content.parentElement.addClass("is-selected");
return;
}
const pathLength = item.path.length - item.name.length;
const matchElements = matches.matches.map((m) => {
return createSpan("suggestion-highlight");
});
for (let i = pathLength; i < item.path.length; i++) {
const match = matches.matches.find((m) => m[0] === i);
if (match) {
const element = matchElements[matches.matches.indexOf(match)];
content.appendChild(element);
element.appendText(item.path.substring(match[0], match[1]));
i += match[1] - match[0] - 1;
continue;
}
content.appendText(item.path[i]);
}
el.createDiv({
cls: "suggestion-note",
text: item.path,
});
}
getItems() {
return this.folders;
}
}
export class FileSuggestionModal extends SuggestionModal<TFile> {
text: TextComponent;
cache: CachedMetadata;
files: TFile[];
file: TFile;
constructor(app: App, input: TextComponent, items: TFile[]) {
super(app, input.inputEl, items);
this.limit = 20;
this.files = [...items];
this.text = input;
this.inputEl.addEventListener("input", () => this.getFile());
}
getFile() {
const v = this.inputEl.value;
const file = this.app.vault.getAbstractFileByPath(v);
if (file === this.file) {
return;
}
if (!(file instanceof TFile)) {
return;
}
this.file = file;
this.onInputChanged();
}
getSelectedItem() {
return this.file;
}
getItemText(item: TFile) {
return item.path;
}
onChooseItem(item: TFile) {
this.file = item;
this.text.setValue(item.path);
this.text.onChanged();
}
selectSuggestion({ item }: FuzzyMatch<TFile>) {
this.file = item;
this.text.setValue(item.path);
this.onClose();
this.text.onChanged();
this.close();
}
renderSuggestion(result: FuzzyMatch<TFile>, el: HTMLElement) {
const { item, match: matches } = result || {};
const content = el.createDiv({
cls: "suggestion-content",
});
if (!item) {
content.setText(this.emptyStateText);
content.parentElement.addClass("is-selected");
return;
}
const pathLength = item.path.length - item.name.length;
const matchElements = matches.matches.map((m) => {
return createSpan("suggestion-highlight");
});
for (let i = pathLength; i < item.path.length; i++) {
const match = matches.matches.find((m) => m[0] === i);
if (match) {
const element = matchElements[matches.matches.indexOf(match)];
content.appendChild(element);
element.appendText(item.path.substring(match[0], match[1]));
i += match[1] - match[0] - 1;
continue;
}
content.appendText(item.path[i]);
}
el.createDiv({
cls: "suggestion-note",
text: item.path,
});
}
getItems() {
return this.files;
}
}

View File

@@ -1,10 +1,12 @@
import { App, FuzzySuggestModal, TFile } from "obsidian";
import { REG_LINKINDEX_INVALIDCHARS } from "../constants/constants";
import { FuzzyMatch, FuzzySuggestModal, setIcon } from "obsidian";
import { AUDIO_TYPES, CODE_TYPES, ICON_NAME, IMAGE_TYPES, REG_LINKINDEX_INVALIDCHARS, VIDEO_TYPES } from "../constants/constants";
import { t } from "../lang/helpers";
import ExcalidrawPlugin from "src/main";
import { getLink } from "src/utils/FileUtils";
import { LinkSuggestion } from "src/types/types";
export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
export class InsertLinkDialog extends FuzzySuggestModal<LinkSuggestion> {
private addText: Function;
private drawingPath: string;
@@ -28,7 +30,7 @@ export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
this.emptyStateText = t("NO_MATCH");
}
getItems(): any[] {
getItems(): LinkSuggestion[] {
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/422
return (
this.app.metadataCache
@@ -39,11 +41,11 @@ export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
);
}
getItemText(item: any): string {
getItemText(item: LinkSuggestion): string {
return item.path + (item.alias ? `|${item.alias}` : "");
}
onChooseItem(item: any): void {
onChooseItem(item: LinkSuggestion): void {
let filepath = item.path;
if (item.file) {
filepath = this.app.metadataCache.fileToLinktext(
@@ -56,6 +58,65 @@ export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
this.addText(getLink(this.plugin,{embed: false, path: filepath, alias: item.alias}), filepath, item.alias);
}
renderSuggestion(result: FuzzyMatch<LinkSuggestion>, itemEl: HTMLElement) {
const { item, match: matches } = result || {};
itemEl.addClass("mod-complex");
const contentEl = itemEl.createDiv("suggestion-content");
const auxEl = itemEl.createDiv("suggestion-aux");
const titleEl = contentEl.createDiv("suggestion-title");
const noteEl = contentEl.createDiv("suggestion-note");
if (!item) {
titleEl.setText(this.emptyStateText);
itemEl.addClass("is-selected");
return;
}
const path = item.file?.path ?? item.path;
const pathLength = path.length - (item.file?.name.length ?? 0);
const matchElements = matches.matches.map((m) => {
return createSpan("suggestion-highlight");
});
const itemText = this.getItemText(item);
for (let i = pathLength; i < itemText.length; i++) {
const match = matches.matches.find((m) => m[0] === i);
if (match) {
const element = matchElements[matches.matches.indexOf(match)];
titleEl.appendChild(element);
element.appendText(itemText.substring(match[0], match[1]));
i += match[1] - match[0] - 1;
continue;
}
titleEl.appendText(itemText[i]);
}
noteEl.setText(path);
if(!item.file) {
setIcon(auxEl, "ghost");
} else if(this.plugin.isExcalidrawFile(item.file)) {
setIcon(auxEl, ICON_NAME);
} else if (item.file.extension === "md") {
setIcon(auxEl, "square-pen");
} else if (IMAGE_TYPES.includes(item.file.extension)) {
setIcon(auxEl, "image");
} else if (VIDEO_TYPES.includes(item.file.extension)) {
setIcon(auxEl, "monitor-play");
} else if (AUDIO_TYPES.includes(item.file.extension)) {
setIcon(auxEl, "file-audio");
} else if (CODE_TYPES.includes(item.file.extension)) {
setIcon(auxEl, "file-code");
} else if (item.file.extension === "canvas") {
setIcon(auxEl, "layout-dashboard");
} else if (item.file.extension === "pdf") {
setIcon(auxEl, "book-open-text");
} else {
auxEl.setText(item.file.extension);
}
}
onClose(): void {
window.setTimeout(()=>{
this.addText = null
@@ -63,9 +124,19 @@ export class InsertLinkDialog extends FuzzySuggestModal<TFile> {
super.onClose();
}
public start(drawingPath: string, addText: Function) {
private inLink: string;
onOpen(): void {
super.onOpen();
if(this.inLink) {
this.inputEl.value = this.inLink;
this.inputEl.dispatchEvent(new Event('input'));
}
}
public start(drawingPath: string, addText: Function, link?: string) {
this.addText = addText;
this.drawingPath = drawingPath;
this.inLink = link;
this.open();
}
}

View File

@@ -3,7 +3,7 @@ import ExcalidrawView from "../ExcalidrawView";
import ExcalidrawPlugin from "../main";
import { getPDFDoc } from "src/utils/FileUtils";
import { Modal, Setting, TextComponent } from "obsidian";
import { FileSuggestionModal } from "./FolderSuggester";
import { FileSuggestionModal } from "../Components/Suggesters/FileSuggestionModal";
import { getEA } from "src";
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
@@ -206,7 +206,11 @@ export class InsertPDFModal extends Modal {
const search = new TextComponent(ce);
search.inputEl.style.width = "100%";
const suggester = new FileSuggestionModal(this.app, search,app.vault.getFiles().filter((f: TFile) => f.extension.toLowerCase() === "pdf"));
const suggester = new FileSuggestionModal(
this.app,
search,this.app.vault.getFiles().filter((f: TFile) => f.extension.toLowerCase() === "pdf"),
this.plugin
);
search.onChange(async () => {
const file = suggester.getSelectedItem();
await setFile(file);

View File

@@ -17,6 +17,42 @@ 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://storage.ko-fi.com/cdn/kofi6.png?v=6" border="0" alt="Buy Me a Coffee at ko-fi.com" height=45></a></div>
`,
"2.6.8":`
## New
- **QoL improvements**:
- Obsidian-link search button in Element Link Editor.
- Add Any File now searches file aliases as well.
- Cosmetic changes to file search modals (display path, show file type icon).
- Text Element cursor-color matches the text color.
- New script in script store: [Image Occlusion](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Image%20Occlusion.md) by [@TrillStones](https://github.com/TrillStones) 🙏
## Fixed
- Excalidraw icon on the **ribbon menu kept reappearing** every time you reopen Obsidian [#2115](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2115)
- In pen mode, when **single-finger panning** is enabled, Excalidraw should still **allow actions with the mouse**.
- When **editing a drawing in split mode** (drawing is on one side, markdown view is on the other), editing the markdown note sometimes causes the drawing to re-zoom and jump away from the selected area.
- Hover-Editor compatibility resolved [2041](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2041)
- ${String.fromCharCode(96)}ExcalidrawAutomate.create() ${String.fromCharCode(96)} will now correctly include the markdown text in templates above Excalidraw Data and below YAML front matter. This also fixes the same issue with the **Deconstruct Selected Element script**.
`,
"2.6.7":`
Hoping to finally move on to 2.7.0... but still have one last bug to fix in 2.6.x!
## Fixed
I misread a line in the Excalidraw package code... ended up breaking image loading in 2.6.6. The icon library script didn't work right, and updating nested drawings caused all images in the scene to be dropped from memory. This led to image-placeholders in exports and broke copy-paste to Excalidraw.com and between drawings. I am surprised no one reported it! 😳
`,
"2.6.6":`
## Fixed
- Images and LaTeX formulas did not update in the scene when the source was changed until the Excalidraw drawing was closed and reopened. [#2105](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2105)
`,
"2.6.5":`
## Fixed
- Text sizing issue in the drawing that is first loaded after Obsidian restarts [#2086](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2086)
- Excalidraw didn't load if there was a file in the Excalidraw folder with a name that starts the same way as the Scripts folder name. [#2095](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2095)
- **OVERSIZED EXCALIDRAW TOOLBAR**: Added a new setting under "Excalidraw Appearance and Behavior > Theme and Styling" called "Limit Obsidian Font Size to Editor Text." This setting is off by default. When enabled, it restricts Obsidian's custom font size adjustments to editor text only, preventing unintended scaling of Excalidraw UI elements and other themes that rely on the default interface font size. Feel free to experiment with this setting to improve Excalidraw UI consistency. However, because this change affects the broader Obsidian UI, it's recommended to turn it off if any layout issues arise. [#2087](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2087)`,
"2.6.4":`
## Fixed
- Error saving when cropping images embedded from a URL (not from a file in the Vault) [#2096](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2096)
`,
"2.6.3":`
<div class="excalidraw-videoWrapper"><div>
<iframe src="https://www.youtube.com/embed/OfUWAvCgbXk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

View File

@@ -2,7 +2,7 @@ import { ButtonComponent, DropdownComponent, TFile, ToggleComponent } from "obsi
import ExcalidrawView from "../ExcalidrawView";
import ExcalidrawPlugin from "../main";
import { Modal, Setting, TextComponent } from "obsidian";
import { FileSuggestionModal } from "./FolderSuggester";
import { FileSuggestionModal } from "../Components/Suggesters/FileSuggestionModal";
import { IMAGE_TYPES, sceneCoordsToViewportCoords, viewportCoordsToSceneCoords, MAX_IMAGE_SIZE, ANIMATED_IMAGE_TYPES, MD_EX_SECTIONS } from "src/constants/constants";
import { insertEmbeddableToView, insertImageToView } from "src/utils/ExcalidrawViewUtils";
import { getEA } from "src";
@@ -146,7 +146,9 @@ export class UniversalInsertFileModal extends Modal {
const suggester = new FileSuggestionModal(
this.app,
search,
this.app.vault.getFiles().filter((f: TFile) => sections?.length > 0 || f!==this.view.file));
this.app.vault.getFiles().filter((f: TFile) => sections?.length > 0 || f!==this.view.file),
this.plugin
);
search.onChange(() => {
file = suggester.getSelectedItem();
updateForm();

View File

@@ -1,3 +1,4 @@
import { FILE } from "dns";
import {
DEVICE,
FRONTMATTER_KEYS,
@@ -10,6 +11,8 @@ declare const PLUGIN_VERSION:string;
// English
export default {
// Sugester
SELECT_FILE_TO_INSERT: "Select a file to insert",
// main.ts
CONVERT_URL_TO_FILE: "Save image from URL to local file",
UNZIP_CURRENT_FILE: "Decompress current Excalidraw file",
@@ -328,6 +331,11 @@ FILENAME_HEAD: "Filename",
"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: "Excalidraw appearance and behavior",
DISPLAY_DESC: "In the 'appearance and behavior' section of Excalidraw Settings, you can fine-tune how Excalidraw appears and behaves. This includes options for dynamic styling, left-handed mode, matching Excalidraw and Obsidian themes, default modes, and more.",
OVERRIDE_OBSIDIAN_FONT_SIZE_NAME: "Limit Obsidian Font Size to Editor Text",
OVERRIDE_OBSIDIAN_FONT_SIZE_DESC:
"Obsidian's custom font size setting affects the entire interface, including Excalidraw and themes that depend on the default font size. " +
"Enabling this option restricts font size changes to editor text, which will improve the look of Excalidraw. " +
"If parts of the UI look incorrect after enabling, try turning this setting off.",
DYNAMICSTYLE_NAME: "Dynamic styling",
DYNAMICSTYLE_DESC:
"Change Excalidraw UI colors to match the canvas color",

View File

@@ -1,3 +1,4 @@
import { FILE } from "dns";
import {
DEVICE,
FRONTMATTER_KEYS,
@@ -6,8 +7,12 @@ import { TAG_AUTOEXPORT, TAG_MDREADINGMODE, TAG_PDFEXPORT } from "src/constants/
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/ModifierkeyHelper";
const CJK_FONTS = "CJK Fonts";
declare const PLUGIN_VERSION:string;
// 简体中文
export default {
// Sugester
SELECT_FILE_TO_INSERT: "选择一个要插入的文件",
// main.ts
CONVERT_URL_TO_FILE: "从 URL 下载图像到本地",
UNZIP_CURRENT_FILE: "解压当前 Excalidraw 文件",
@@ -325,7 +330,12 @@ FILENAME_HEAD: "文件名",
"该选项在兼容模式(即非 Excalidraw 专用 Markdown 文件)下不会生效。<br>" +
"<b>开启:</b>使用 .excalidraw.md 作为扩展名。<br><b>关闭:</b>使用 .md 作为扩展名。",
DISPLAY_HEAD: "界面 & 行为",
DISPLAY_DESC: "包括:左手模式,主题匹配,缩放,激光笔工具,修饰键等的设置。",
DISPLAY_DESC: "在 Excalidraw 设置的 '外观和行为' 部分,您可以微调 Excalidraw 的外观和行为。这包括动态样式、左手模式、匹配 Excalidraw 和 Obsidian 主题、默认模式等选项。",
OVERRIDE_OBSIDIAN_FONT_SIZE_NAME : "限制 Obsidian 字体大小为编辑器文本" ,
OVERRIDE_OBSIDIAN_FONT_SIZE_DESC :
"Obsidian 的自定义字体大小设置会影响整个界面,包括 Excalidraw 和依赖默认字体大小的主题。" +
"启用此选项将限制字体大小更改为编辑器文本,这将改善 Excalidraw 的外观。" +
"如果启用后发现界面的某些部分看起来不正确,请尝试关闭此设置。" ,
DYNAMICSTYLE_NAME: "动态样式",
DYNAMICSTYLE_DESC:
"根据画布颜色自动调节 Excalidraw 界面颜色",
@@ -870,6 +880,7 @@ FILENAME_HEAD: "文件名",
对此带来的不便,我深表歉意。
</p>
`,
//ObsidianMenu.tsx
GOTO_FULLSCREEN: "进入全屏模式",
EXIT_FULLSCREEN: "退出全屏模式",
@@ -958,4 +969,7 @@ FILENAME_HEAD: "文件名",
IPM_GROUP_PAGES_DESC: "这将把所有页面建立为一个单独的组。如果您在导入后锁定页面,建议使用此方法,因为这样可以更方便地解锁整个组,而不是逐个解锁。",
IPM_SELECT_PDF: "请选择一个 PDF 文件",
};
//Utils.ts
UPDATE_AVAILABLE: `Excalidraw 的新版本已在社区插件中可用。\n\n您正在使用 ${PLUGIN_VERSION}\n最新版本是`,
ERROR_PNG_TOO_LARGE: "导出 PNG 时出错 - PNG 文件过大,请尝试较小的分辨率",
};

View File

@@ -19,7 +19,6 @@ import {
Workspace,
Editor,
MarkdownFileInfo,
loadMermaid,
} from "obsidian";
import {
BLANK_DRAWING,
@@ -113,7 +112,7 @@ import {
legacyExcalidrawPopoverObserver,
} from "./MarkdownPostProcessor";
import { FieldSuggester } from "./dialogs/FieldSuggester";
import { FieldSuggester } from "./Components/Suggesters/FieldSuggester";
import { ReleaseNotes } from "./dialogs/ReleaseNotes";
import { Packages } from "./types/types";
import { PreviewImageType } from "./utils/UtilTypes";
@@ -145,7 +144,6 @@ import { WeakArray } from "./utils/WeakArray";
import { getCJKDataURLs } from "./utils/CJKLoader";
import { ExcalidrawLoading, switchToExcalidraw } from "./dialogs/ExcalidrawLoading";
import { insertImageToView } from "./utils/ExcalidrawViewUtils";
import tr from "./lang/locale/tr";
declare let EXCALIDRAW_PACKAGE:string;
declare let REACT_PACKAGES:string;
@@ -153,7 +151,8 @@ declare const unpackExcalidraw: Function;
declare let react:any;
declare let reactDOM:any;
declare let excalidrawLib: typeof ExcalidrawLib;
declare let PLUGIN_VERSION:string;
declare const PLUGIN_VERSION:string;
declare const INITIAL_TIMESTAMP: number;
export default class ExcalidrawPlugin extends Plugin {
public eaInstances = new WeakArray<ExcalidrawAutomate>();
@@ -199,13 +198,17 @@ export default class ExcalidrawPlugin extends Plugin {
//if set, the next time this file is opened it will be opened as markdown
public forceToOpenInMarkdownFilepath: string = null;
//private slob:string;
private ribbonIcon:HTMLElement;
public loadTimestamp:number;
private isLocalCJKFontAvailabe:boolean = undefined
public isReady = false;
private startupAnalytics: string[] = [];
private lastLogTimestamp: number;
private settingsReady: boolean = false;
constructor(app: App, manifest: PluginManifest) {
super(app, manifest);
this.loadTimestamp = INITIAL_TIMESTAMP;
this.lastLogTimestamp = this.loadTimestamp;
this.packageMap.set(window,{react, reactDOM, excalidrawLib});
this.filesMaster = new Map<
FileId,
@@ -219,6 +222,16 @@ export default class ExcalidrawPlugin extends Plugin {
}*/
}
private logStartupEvent(message:string) {
const timestamp = Date.now();
this.startupAnalytics.push(`${message}\nTotal: ${timestamp - this.loadTimestamp}ms Delta: ${timestamp - this.lastLogTimestamp}ms\n`);
this.lastLogTimestamp = timestamp;
}
public printStarupBreakdown() {
console.log(`Excalidraw ${PLUGIN_VERSION} startup breakdown:\n`+this.startupAnalytics.join("\n"));
}
get locale() {
return LOCALE;
}
@@ -352,6 +365,7 @@ export default class ExcalidrawPlugin extends Plugin {
}
async onload() {
this.logStartupEvent("Plugin Constructor ready, starting onload()");
this.registerView(
VIEW_TYPE_EXCALIDRAW,
(leaf: WorkspaceLeaf) => {
@@ -365,13 +379,50 @@ export default class ExcalidrawPlugin extends Plugin {
//Compatibility mode with .excalidraw files
this.registerExtensions(["excalidraw"], VIEW_TYPE_EXCALIDRAW);
await this.loadSettings({reEnableAutosave:true});
addIcon(ICON_NAME, EXCALIDRAW_ICON);
addIcon(SCRIPTENGINE_ICON_NAME, SCRIPTENGINE_ICON);
addIcon(EXPORT_IMG_ICON_NAME, EXPORT_IMG_ICON);
this.addRibbonIcon(ICON_NAME, t("CREATE_NEW"), this.actionRibbonClick.bind(this));
try {
this.loadSettings({reEnableAutosave:true})
.then(this.onloadCheckForOnceOffSettingsUpdates.bind(this));
} catch (e) {
new Notice("Error loading plugin settings", 6000);
console.error("Error loading plugin settings", e);
}
this.logStartupEvent("Settings loaded");
try {
// need it her for ExcaliBrain
this.ea = initExcalidrawAutomate(this);
} catch (e) {
new Notice("Error initializing Excalidraw Automate", 6000);
console.error("Error initializing Excalidraw Automate", e);
}
this.logStartupEvent("Excalidraw Automate initialized");
try {
//Licat: Are you registering your post processors in onLayoutReady? You should register them in onload instead
this.addMarkdownPostProcessor();
} catch (e) {
new Notice("Error adding markdown post processor", 6000);
console.error("Error adding markdown post processor", e);
}
this.logStartupEvent("Markdown post processor added");
this.app.workspace.onLayoutReady(this.onloadOnLayoutReady.bind(this));
this.logStartupEvent("Workspace ready event handler added");
}
private async onloadCheckForOnceOffSettingsUpdates() {
const updateSettings = !this.settings.onceOffCompressFlagReset || !this.settings.onceOffGPTVersionReset;
if(!this.settings.onceOffCompressFlagReset) {
this.settings.compress = true;
this.settings.onceOffCompressFlagReset = true;
}
if(!this.settings.onceOffGPTVersionReset) {
this.settings.onceOffGPTVersionReset = true;
if(this.settings.openAIDefaultVisionModel === "gpt-4-vision-preview") {
this.settings.openAIDefaultVisionModel = "gpt-4o";
}
@@ -380,118 +431,204 @@ export default class ExcalidrawPlugin extends Plugin {
await this.saveSettings();
}
this.addSettingTab(new ExcalidrawSettingTab(this.app, this));
this.ea = await initExcalidrawAutomate(this);
this.settingsReady = true;
}
//Licat: Are you registering your post processors in onLayoutReady? You should register them in onload instead
this.addMarkdownPostProcessor();
this.app.workspace.onLayoutReady(async () => {
private async onloadOnLayoutReady() {
this.loadTimestamp = Date.now();
this.lastLogTimestamp = this.loadTimestamp;
this.logStartupEvent("\n----------------------------------\nWorkspace onLayoutReady event fired (these actions are outside the plugin initialization)");
await this.awaitSettings();
this.logStartupEvent("Settings awaited");
try {
unpackExcalidraw();
excalidrawLib = window.eval.call(window,`(function() {${EXCALIDRAW_PACKAGE};return ExcalidrawLib;})()`);
this.packageMap.set(window,{react, reactDOM, excalidrawLib});
updateExcalidrawLib();
} catch (e) {
new Notice("Error loading the Excalidraw package", 6000);
console.error("Error loading the Excalidraw package", e);
}
this.logStartupEvent("Excalidraw package unpacked");
try {
initCompressionWorker();
this.loadTimestamp = Date.now();
addIcon(ICON_NAME, EXCALIDRAW_ICON);
addIcon(SCRIPTENGINE_ICON_NAME, SCRIPTENGINE_ICON);
addIcon(EXPORT_IMG_ICON_NAME, EXPORT_IMG_ICON);
} catch (e) {
new Notice("Error initializing compression worker", 6000);
console.error("Error initializing compression worker", e);
}
this.logStartupEvent("Compression worker initialized");
try {
this.excalidrawConfig = new ExcalidrawConfig(this);
await loadMermaid();
this.addThemeObserver();
} catch (e) {
new Notice("Error initializing Excalidraw config", 6000);
console.error("Error initializing Excalidraw config", e);
}
this.logStartupEvent("Excalidraw config initialized");
try {
this.addThemeObserver();
} catch (e) {
new Notice("Error adding theme observer", 6000);
console.error("Error adding theme observer", e);
}
this.logStartupEvent("Theme observer added");
try {
//inspiration taken from kanban:
//https://github.com/mgmeyers/obsidian-kanban/blob/44118e25661bff9ebfe54f71ae33805dc88ffa53/src/main.ts#L267
this.registerMonkeyPatches();
} catch (e) {
new Notice("Error registering monkey patches", 6000);
console.error("Error registering monkey patches", e);
}
this.logStartupEvent("Monkey patches registered");
try {
this.stylesManager = new StylesManager(this);
this.scriptEngine = new ScriptEngine(this);
await this.initializeFonts();
imageCache.initializeDB(this);
} catch (e) {
new Notice("Error initializing styles manager", 6000);
console.error("Error initializing styles manager", e);
}
this.logStartupEvent("Styles manager initialized");
try {
this.scriptEngine = new ScriptEngine(this);
} catch (e) {
new Notice("Error initializing script engine", 6000);
console.error("Error initializing script engine", e);
}
this.logStartupEvent("Script engine initialized");
try {
await this.initializeFonts();
} catch (e) {
new Notice("Error initializing fonts", 6000);
console.error("Error initializing fonts", e);
}
this.logStartupEvent("Fonts initialized");
try {
imageCache.initializeDB(this);
} catch (e) {
new Notice("Error initializing image cache", 6000);
console.error("Error initializing image cache", e);
}
this.logStartupEvent("Image cache initialized");
try {
this.isReady = true;
switchToExcalidraw(this.app);
this.switchToExcalidarwAfterLoad();
} catch (e) {
new Notice("Error switching views to Excalidraw", 6000);
console.error("Error switching views to Excalidraw", e);
}
this.logStartupEvent("Switched to Excalidraw views");
try {
if (this.settings.showReleaseNotes) {
//I am repurposing imageElementNotice, if the value is true, this means the plugin was just newly installed to Obsidian.
const obsidianJustInstalled = (this.settings.previousRelease === "0.0.0") || !this.settings.previousRelease;
try {
if (this.settings.showReleaseNotes) {
//I am repurposing imageElementNotice, if the value is true, this means the plugin was just newly installed to Obsidian.
const obsidianJustInstalled = (this.settings.previousRelease === "0.0.0") || !this.settings.previousRelease;
if (isVersionNewerThanOther(PLUGIN_VERSION, this.settings.previousRelease ?? "0.0.0")) {
new ReleaseNotes(
this.app,
this,
obsidianJustInstalled ? null : PLUGIN_VERSION,
).open();
}
if (isVersionNewerThanOther(PLUGIN_VERSION, this.settings.previousRelease ?? "0.0.0")) {
new ReleaseNotes(
this.app,
this,
obsidianJustInstalled ? null : PLUGIN_VERSION,
).open();
}
} catch (e) {
new Notice("Error opening release notes", 6000);
console.error("Error opening release notes", e);
}
} catch (e) {
new Notice("Error opening release notes", 6000);
console.error("Error opening release notes", e);
}
this.logStartupEvent("Release notes opened");
//initialization that can happen after Excalidraw views are initialized
try {
this.registerEventListeners();
} catch (e) {
new Notice("Error registering event listeners", 6000);
console.error("Error registering event listeners", e);
}
try {
this.runStartupScript();
} catch (e) {
new Notice("Error running startup script", 6000);
console.error("Error running startup script", e);
}
try {
this.editorHandler = new EditorHandler(this);
this.editorHandler.setup();
} catch (e) {
new Notice("Error setting up editor handler", 6000);
console.error("Error setting up editor handler", e);
}
try {
this.registerInstallCodeblockProcessor();
} catch (e) {
new Notice("Error registering script install-codeblock processor", 6000);
console.error("Error registering script install-codeblock processor", e);
}
//---------------------------------------------------------------------
//initialization that can happen after Excalidraw views are initialized
//---------------------------------------------------------------------
try {
this.registerEventListeners();
} catch (e) {
new Notice("Error registering event listeners", 6000);
console.error("Error registering event listeners", e);
}
this.logStartupEvent("Event listeners registered");
try {
this.experimentalFileTypeDisplayToggle(this.settings.experimentalFileType);
} catch (e) {
new Notice("Error setting up experimental file type display", 6000);
console.error("Error setting up experimental file type display", e);
}
try {
this.registerCommands();
} catch (e) {
new Notice("Error registering commands", 6000);
console.error("Error registering commands", e);
}
try {
this.registerEditorSuggest(new FieldSuggester(this));
} catch (e) {
new Notice("Error registering editor suggester", 6000);
console.error("Error registering editor suggester", e);
}
try {
this.setPropertyTypes();
} catch (e) {
new Notice("Error setting up property types", 6000);
console.error("Error setting up property types", e);
}
try {
this.taskbone = new Taskbone(this);
} catch (e) {
new Notice("Error setting up taskbone", 6000);
console.error("Error setting up taskbone", e);
}
});
try {
this.runStartupScript();
} catch (e) {
new Notice("Error running startup script", 6000);
console.error("Error running startup script", e);
}
this.logStartupEvent("Startup script run");
try {
this.editorHandler = new EditorHandler(this);
this.editorHandler.setup();
} catch (e) {
new Notice("Error setting up editor handler", 6000);
console.error("Error setting up editor handler", e);
}
this.logStartupEvent("Editor handler initialized");
try {
this.registerInstallCodeblockProcessor();
} catch (e) {
new Notice("Error registering script install-codeblock processor", 6000);
console.error("Error registering script install-codeblock processor", e);
}
this.logStartupEvent("Script install-codeblock processor registered");
try {
this.experimentalFileTypeDisplayToggle(this.settings.experimentalFileType);
} catch (e) {
new Notice("Error setting up experimental file type display", 6000);
console.error("Error setting up experimental file type display", e);
}
this.logStartupEvent("Experimental file type display set");
try {
this.registerCommands();
} catch (e) {
new Notice("Error registering commands", 6000);
console.error("Error registering commands", e);
}
this.logStartupEvent("Commands registered");
try {
this.registerEditorSuggest(new FieldSuggester(this));
} catch (e) {
new Notice("Error registering editor suggester", 6000);
console.error("Error registering editor suggester", e);
}
this.logStartupEvent("Editor suggester registered");
try {
this.setPropertyTypes();
} catch (e) {
new Notice("Error setting up property types", 6000);
console.error("Error setting up property types", e);
}
this.logStartupEvent("Property types set");
try {
this.taskbone = new Taskbone(this);
} catch (e) {
new Notice("Error setting up taskbone", 6000);
console.error("Error setting up taskbone", e);
}
this.logStartupEvent("Taskbone set up");
}
public async awaitSettings() {
let counter = 0;
while(!this.settingsReady && counter < 150) {
await sleep(20);
}
}
public async awaitInit() {
let counter = 0;
@@ -996,8 +1133,6 @@ export default class ExcalidrawPlugin extends Plugin {
this.importSVGDialog = new ImportSVGDialog(this);
this.insertMDDialog = new InsertMDDialog(this);
this.ribbonIcon = this.addRibbonIcon(ICON_NAME, t("CREATE_NEW"), this.actionRibbonClick.bind(this));
const createNewAction = (e: MouseEvent | KeyboardEvent, file: TFile) => {
let folderpath = file.path;
if (file instanceof TFile) {
@@ -2918,6 +3053,12 @@ export default class ExcalidrawPlugin extends Plugin {
}
this.leafChangeTimeout = window.setTimeout(()=>{this.leafChangeTimeout = null;},1000);
if(this.settings.overrideObsidianFontSize) {
if(leaf.view && (leaf.view.getViewType() === VIEW_TYPE_EXCALIDRAW)) {
document.documentElement.style.fontSize = "";
}
}
const previouslyActiveEV = this.activeExcalidrawView;
const newActiveviewEV: ExcalidrawView =
leaf.view instanceof ExcalidrawView ? leaf.view : null;
@@ -3155,7 +3296,11 @@ export default class ExcalidrawPlugin extends Plugin {
}
//if the user hasn't touched the file for 5 minutes, don't synchronize, reload.
//this is to avoid complex sync scenarios of multiple remote changes outside an active collaboration session
if(excalidrawView.lastSaveTimestamp + 300000 < Date.now()) {
const activeView = this.app.workspace.activeLeaf.view;
const isEditingMarkdownSideInSplitView = (activeView !== excalidrawView) &&
activeView instanceof MarkdownView && activeView.file === excalidrawView.file;
if(!isEditingMarkdownSideInSplitView && (excalidrawView.lastSaveTimestamp + 300000 < Date.now())) {
excalidrawView.reload(true, excalidrawView.file);
return;
}
@@ -3403,11 +3548,6 @@ export default class ExcalidrawPlugin extends Plugin {
window.clearTimeout(versionUpdateCheckTimer);
}
if(this.ribbonIcon) {
this.ribbonIcon.remove();
this.ribbonIcon = null;
}
if(this.scriptEngine) {
this.scriptEngine.destroy();
this.scriptEngine = null;
@@ -3509,7 +3649,7 @@ export default class ExcalidrawPlugin extends Plugin {
EXCALIDRAW_PACKAGE = "";
REACT_PACKAGES = "";
//pluginPackages = null;
PLUGIN_VERSION = null;
//PLUGIN_VERSION = null;
//@ts-ignore
delete window.PolyBool;
this.deletePackage(window);

View File

@@ -41,6 +41,7 @@ import { Rank } from "./menu/ActionIcons";
import { TAG_AUTOEXPORT, TAG_MDREADINGMODE, TAG_PDFEXPORT } from "src/constants/constSettingsTags";
import { HotkeyEditor } from "./dialogs/HotkeyEditor";
import { getExcalidrawViews } from "./utils/ObsidianUtils";
import de from "./lang/locale/de";
export interface ExcalidrawSettings {
folder: string;
@@ -76,6 +77,7 @@ export interface ExcalidrawSettings {
previewMatchObsidianTheme: boolean;
width: string;
height: string;
overrideObsidianFontSize: boolean;
dynamicStyling: DynamicStyle;
isLeftHanded: boolean;
iframeMatchExcalidrawTheme: boolean;
@@ -253,6 +255,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
previewMatchObsidianTheme: false,
width: "400",
height: "",
overrideObsidianFontSize: false,
dynamicStyling: "colorful",
isLeftHanded: false,
iframeMatchExcalidrawTheme: true,
@@ -516,6 +519,12 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}
async hide() {
if(this.plugin.settings.overrideObsidianFontSize) {
document.documentElement.style.fontSize = "";
} else if(!document.documentElement.style.fontSize) {
document.documentElement.style.fontSize = getComputedStyle(document.body).getPropertyValue("--font-text-size");
}
this.plugin.settings.scriptFolderPath = normalizePath(
this.plugin.settings.scriptFolderPath,
);
@@ -1163,6 +1172,18 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
cls: "excalidraw-setting-h3",
});
new Setting(detailsEl)
.setName(t("OVERRIDE_OBSIDIAN_FONT_SIZE_NAME"))
.setDesc(fragWithHTML(t("OVERRIDE_OBSIDIAN_FONT_SIZE_DESC")))
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.overrideObsidianFontSize)
.onChange((value) => {
this.plugin.settings.overrideObsidianFontSize = value;
this.applySettingsUpdate();
}),
);
new Setting(detailsEl)
.setName(t("DYNAMICSTYLE_NAME"))
.setDesc(fragWithHTML(t("DYNAMICSTYLE_DESC")))

View File

@@ -1,3 +1,4 @@
import { TFile } from "obsidian";
import { ExcalidrawAutomate } from "../ExcalidrawAutomate";
import { ExcalidrawLib } from "../ExcalidrawLib";
@@ -33,6 +34,12 @@ export type DeviceType = {
export type Point = [number, number];
export type LinkSuggestion = {
file: TFile;
path: string;
alias?: string;
}
declare global {
interface Window {
ExcalidrawAutomate: ExcalidrawAutomate;

View File

@@ -13,6 +13,7 @@ export const setDynamicStyle = (
view: ExcalidrawView, //the excalidraw view
color: string,
dynamicStyle: DynamicStyle,
textBackgroundColor?: string,
) => {
if(dynamicStyle === "none") {
view.excalidrawContainer?.removeAttribute("style");
@@ -116,7 +117,9 @@ export const setDynamicStyle = (
[`--h3-color`]: str(text),
[`--h4-color`]: str(text),
[`color`]: str(text),
['--excalidraw-caret-color']: str(isLightTheme ? text : cmBG()),
['--excalidraw-caret-color']: textBackgroundColor
? str(isLightTheme ? invertColor(textBackgroundColor) : ea.getCM(textBackgroundColor))
: str(isLightTheme ? text : cmBG()),
[`--select-highlight-color`]: str(gray1()),
[`--color-gray-90`]: str(isDark?text.darkerBy(5):text.lighterBy(5)), //search background
[`--color-gray-80`]: str(isDark?text.darkerBy(10):text.lighterBy(10)), //frame