mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
1.7.21
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.7.20",
|
||||
"version": "1.7.21",
|
||||
"minAppVersion": "0.15.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -422,7 +422,7 @@ export class EmbeddedFilesLoader {
|
||||
let fontName = plugin.settings.mdFont;
|
||||
if (
|
||||
fileCache?.frontmatter &&
|
||||
fileCache.frontmatter[FRONTMATTER_KEY_FONT] != null
|
||||
Boolean(fileCache.frontmatter[FRONTMATTER_KEY_FONT])
|
||||
) {
|
||||
fontName = fileCache.frontmatter[FRONTMATTER_KEY_FONT];
|
||||
}
|
||||
@@ -442,6 +442,13 @@ export class EmbeddedFilesLoader {
|
||||
fontName = font.fontName;
|
||||
}
|
||||
|
||||
if (
|
||||
fileCache?.frontmatter &&
|
||||
fileCache.frontmatter["banner"] !== null
|
||||
) {
|
||||
text = text.replace(/banner:\s*.*/,""); //patch https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/814
|
||||
}
|
||||
|
||||
const fontColor = fileCache?.frontmatter
|
||||
? fileCache.frontmatter[FRONTMATTER_KEY_FONTCOLOR] ??
|
||||
plugin.settings.mdFontColor
|
||||
|
||||
@@ -854,6 +854,7 @@ export class ExcalidrawData {
|
||||
let linkIcon = false;
|
||||
let urlIcon = false;
|
||||
let parts;
|
||||
text = this.parseCheckbox(text);
|
||||
if (text.match(REG_LINKINDEX_HYPERLINK)) {
|
||||
link = text;
|
||||
urlIcon = true;
|
||||
@@ -869,8 +870,8 @@ export class ExcalidrawData {
|
||||
}
|
||||
if (REGEX_LINK.isTransclusion(parts)) {
|
||||
//transclusion //parts.value[1] || parts.value[4]
|
||||
const contents = (await this.getTransclusion(REGEX_LINK.getLink(parts)))
|
||||
.contents;
|
||||
const contents = this.parseCheckbox((await this.getTransclusion(REGEX_LINK.getLink(parts)))
|
||||
.contents);
|
||||
outString +=
|
||||
text.substring(position, parts.value.index) +
|
||||
wrapText(
|
||||
@@ -907,6 +908,16 @@ export class ExcalidrawData {
|
||||
return { parsed: outString, link };
|
||||
}
|
||||
|
||||
private parseCheckbox(text:string):string {
|
||||
return this.plugin.settings.parseTODO
|
||||
? text
|
||||
.replaceAll(/^- \[\s] /g,`${this.plugin.settings.todo} `)
|
||||
.replaceAll(/\n- \[\s] /g,`\n${this.plugin.settings.todo} `)
|
||||
.replaceAll(/^- \[[^\s]] /g,`${this.plugin.settings.done} `)
|
||||
.replaceAll(/\n- \[[^\s]] /g,`\n${this.plugin.settings.done} `)
|
||||
: text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a quick parse of the raw text. Returns the parsed string if raw text does not include a transclusion.
|
||||
* Return null if raw text includes a transclusion.
|
||||
@@ -936,6 +947,7 @@ export class ExcalidrawData {
|
||||
let linkIcon = false;
|
||||
let urlIcon = false;
|
||||
let parts;
|
||||
text = this.parseCheckbox(text);
|
||||
if (text.match(REG_LINKINDEX_HYPERLINK)) {
|
||||
link = text;
|
||||
urlIcon = true;
|
||||
@@ -1065,7 +1077,7 @@ export class ExcalidrawData {
|
||||
const equation = this.getEquation(fileId);
|
||||
//const equation = this.equations.get(fileId as FileId);
|
||||
//images should have a single reference, but equations and markdown embeds should have as many as instances of the file in the scene
|
||||
if(file && (file.file.extension !== "md" || this.plugin.isExcalidrawFile(file.file))) {
|
||||
if(file && file.file && (file.file.extension !== "md" || this.plugin.isExcalidrawFile(file.file))) {
|
||||
return;
|
||||
}
|
||||
const newId = fileid();
|
||||
|
||||
@@ -457,6 +457,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
return;
|
||||
}
|
||||
this.semaphores.saving = true;
|
||||
|
||||
//if there were no changes to the file super save will not save
|
||||
//and consequently main.ts modifyEventHandler will not fire
|
||||
//this.reload will not be called
|
||||
//triggerReload is used to flag if there were no changes but file should be reloaded anyway
|
||||
let triggerReload:boolean = false;
|
||||
|
||||
if (
|
||||
!this.getScene ||
|
||||
@@ -500,6 +506,8 @@ export default class ExcalidrawView extends TextFileView {
|
||||
|
||||
this.semaphores.preventReload = preventReload;
|
||||
await super.save();
|
||||
triggerReload = (this.lastSaveTimestamp === this.file.stat.mtime) &&
|
||||
!preventReload && forcesave;
|
||||
this.lastSaveTimestamp = this.file.stat.mtime;
|
||||
this.clearDirty();
|
||||
|
||||
@@ -512,7 +520,8 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.semaphores.autosaving && !this.semaphores.viewunload) {
|
||||
// !triggerReload means file has not changed. No need to re-export
|
||||
if (!triggerReload && !this.semaphores.autosaving && !this.semaphores.viewunload) {
|
||||
const autoexportPreference = this.excalidrawData.autoexportPreference;
|
||||
if (
|
||||
(autoexportPreference === AutoexportPreference.inherit && this.plugin.settings.autoexportSVG) ||
|
||||
@@ -542,6 +551,9 @@ export default class ExcalidrawView extends TextFileView {
|
||||
warningUnknowSeriousError();
|
||||
}
|
||||
this.semaphores.saving = false;
|
||||
if(triggerReload) {
|
||||
this.reload(true, this.file);
|
||||
}
|
||||
}
|
||||
|
||||
// get the new file content
|
||||
@@ -1003,9 +1015,15 @@ export default class ExcalidrawView extends TextFileView {
|
||||
DISK_ICON_NAME,
|
||||
t("FORCE_SAVE"),
|
||||
async () => {
|
||||
if (this.semaphores.autosaving) {
|
||||
if (this.semaphores.autosaving || this.semaphores.saving) {
|
||||
new Notice("Force Save aborted because saving is in progress)")
|
||||
return;
|
||||
}
|
||||
if(this.preventReloadResetTimer) {
|
||||
clearTimeout(this.preventReloadResetTimer);
|
||||
this.preventReloadResetTimer = null;
|
||||
}
|
||||
this.semaphores.preventReload = false;
|
||||
this.semaphores.forceSaving = true;
|
||||
await this.save(false, true);
|
||||
this.plugin.triggerEmbedUpdates();
|
||||
|
||||
@@ -16,6 +16,31 @@ export const RELEASE_NOTES: { [k: string]: string } = {
|
||||
I develop this plugin as a hobby, spending most of my free time doing this. If you'd like to contribute to the on-going work, I have a simple membership scheme with Bronze, Silver and Gold tiers. Many of you have already bought me a coffee. THANK YOU! It really means a lot to me! If you find this plugin valuable, please consider supporting me.
|
||||
|
||||
<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.7.21":`
|
||||
# New from Excalidraw.com
|
||||
- Image-mirroring in export preview and in exported SVG [#5700](https://github.com/excalidraw/excalidraw/pull/5700), [#811](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/811), [#617](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/617)
|
||||
|
||||
# New
|
||||
- Ctrl+s will force-save your drawing and update all your transclusions
|
||||
- Added setting to parse ${String.fromCharCode(96)}- [ ] ${String.fromCharCode(96)} and ${String.fromCharCode(96)}- [x] ${String.fromCharCode(96)} todo items. Parsing is disabled by default. This feature can be found under "Links and Transclusions" in Plugin Settings. [#819](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/819)
|
||||
|
||||

|
||||
|
||||
<iframe src="https://user-images.githubusercontent.com/14358394/192151120-3c61c822-0352-4ba7-9900-b38078fb373c.mp4" title="Demo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div></div>
|
||||
|
||||
- Added new scripts to the script library
|
||||
- [Rename Image](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Rename%20Image.md)
|
||||
- [Text Arch](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Text%20Arch.md)
|
||||
|
||||
<iframe src="https://user-images.githubusercontent.com/14358394/192151105-78c0115b-4e30-4296-b647-e3c05851a48f.mp4" title="Demo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div></div>
|
||||
|
||||
# Fixed
|
||||
- Fixed toast message to display script name on press and hold on mobile and iPad.
|
||||
- Fixed save error when the embedded image file is not found (i.e. it was moved, renamed, or deleted)
|
||||
|
||||
`,
|
||||
"1.7.20":`
|
||||
# New from Excalidraw.com
|
||||
|
||||
@@ -77,7 +77,7 @@ export default {
|
||||
FILE_DOES_NOT_EXIST:
|
||||
"File does not exist. Hold down ALT (or ALT+SHIFT) and CLICK link button to create a new file.",
|
||||
FORCE_SAVE:
|
||||
"Force-save to update transclusions in adjacent panes.\n(Check autosave settings in plugin settings.)",
|
||||
"Save (will also update transclusions)",
|
||||
RAW: "Change to PREVIEW mode (only effects text-elements with links or transclusions)",
|
||||
PARSED:
|
||||
"Change to RAW mode (only effects text-elements with links or transclusions)",
|
||||
@@ -226,6 +226,12 @@ export default {
|
||||
"In PREVIEW mode, if the Text Element contains a URL link, precede the text with these characters. " +
|
||||
"You can override this setting for a specific drawing by adding <code>"
|
||||
}${FRONTMATTER_KEY_CUSTOM_URL_PREFIX}: "🌐 "</code> to the file's frontmatter.`,
|
||||
PARSE_TODO_NAME: "Parse todo",
|
||||
PARSE_TODO_DESC: "Convert '- [ ] ' and '- [x] ' to checkpox and tick in the box.",
|
||||
TODO_NAME: "Open TODO icon",
|
||||
TODO_DESC: "Icon to use for open TODO items",
|
||||
DONE_NAME: "Completed TODO icon",
|
||||
DONE_DESC: "Icon to use for completed TODO items",
|
||||
HOVERPREVIEW_NAME: "Hover preview without CTRL/CMD 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 CTRL/CMD key. " +
|
||||
|
||||
48
src/main.ts
48
src/main.ts
@@ -15,7 +15,8 @@ import {
|
||||
loadMathJax,
|
||||
request,
|
||||
MetadataCache,
|
||||
FrontMatterCache
|
||||
FrontMatterCache,
|
||||
Command
|
||||
} from "obsidian";
|
||||
import {
|
||||
BLANK_DRAWING,
|
||||
@@ -163,6 +164,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
public fourthFontDef: string = VIRGIL_FONT;
|
||||
private packageMap: WeakMap<Window,Packages> = new WeakMap<Window,Packages>();
|
||||
public leafChangeTimeout: NodeJS.Timeout = null;
|
||||
private forceSaveCommand:Command;
|
||||
|
||||
constructor(app: App, manifest: PluginManifest) {
|
||||
super(app, manifest);
|
||||
@@ -362,6 +364,29 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
});
|
||||
}
|
||||
|
||||
private forceSaveActiveView(checking:boolean):boolean {
|
||||
if (checking) {
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView));
|
||||
}
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
(async()=>{
|
||||
if (view.semaphores.autosaving) {
|
||||
return;
|
||||
}
|
||||
view.semaphores.forceSaving = true;
|
||||
await view.save(false, true);
|
||||
view.plugin.triggerEmbedUpdates();
|
||||
view.loadSceneFiles();
|
||||
view.semaphores.forceSaving = false;
|
||||
new Notice("Save successful", 1000);
|
||||
})();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private registerInstallCodeblockProcessor() {
|
||||
const codeblockProcessor = async (source: string, el: HTMLElement) => {
|
||||
//Button next to the "List of available scripts" at the top
|
||||
@@ -951,6 +976,13 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
},
|
||||
});
|
||||
|
||||
this.forceSaveCommand = this.addCommand({
|
||||
id: "save",
|
||||
hotkeys: [{modifiers: ["Ctrl"], key:"s"}], //See also Poposcope
|
||||
name: t("FORCE_SAVE"),
|
||||
checkCallback: (checking:boolean) => this.forceSaveActiveView(checking),
|
||||
})
|
||||
|
||||
this.addCommand({
|
||||
id: "toggle-lock",
|
||||
hotkeys: [{ modifiers: ["Ctrl" || "Meta", "Shift"], key: "e" }],
|
||||
@@ -1624,8 +1656,20 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
if (newActiveviewEV) {
|
||||
const scope = this.app.keymap.getRootScope();
|
||||
const handler = scope.register(["Mod"], "Enter", () => true);
|
||||
const overridSaveShortcut = (
|
||||
this.forceSaveCommand &&
|
||||
this.forceSaveCommand.hotkeys[0].key === "s" &&
|
||||
this.forceSaveCommand.hotkeys[0].modifiers.includes("Ctrl")
|
||||
)
|
||||
const self = this;
|
||||
const saveHandler = overridSaveShortcut
|
||||
? scope.register(["Ctrl"], "s", () => self.forceSaveActiveView(false))
|
||||
: undefined;
|
||||
scope.keys.unshift(scope.keys.pop()); // Force our handler to the front of the list
|
||||
self.popScope = () => scope.unregister(handler);
|
||||
self.popScope = () => {
|
||||
scope.unregister(handler);
|
||||
Boolean(saveHandler) && scope.unregister(saveHandler);
|
||||
}
|
||||
}
|
||||
};
|
||||
self.registerEvent(
|
||||
|
||||
@@ -42,7 +42,7 @@ export class ActionButton extends React.Component<ButtonProps, ButtonState> {
|
||||
onPointerDown={() => {
|
||||
this.toastMessageTimeout = window.setTimeout(
|
||||
() =>
|
||||
this.props.view.excalidrawAPI?.setToastMessage(this.props.title),
|
||||
this.props.view.excalidrawAPI?.setToast({message:this.props.title}),
|
||||
300,
|
||||
);
|
||||
}}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
normalizePath,
|
||||
PluginSettingTab,
|
||||
Setting,
|
||||
TextComponent,
|
||||
TFile,
|
||||
} from "obsidian";
|
||||
import { VIEW_TYPE_EXCALIDRAW } from "./Constants";
|
||||
@@ -48,6 +49,9 @@ export interface ExcalidrawSettings {
|
||||
showLinkBrackets: boolean;
|
||||
linkPrefix: string;
|
||||
urlPrefix: string;
|
||||
parseTODO: boolean;
|
||||
todo: string;
|
||||
done: string;
|
||||
hoverPreviewWithoutCTRL: boolean;
|
||||
linkOpacity: number;
|
||||
allowCtrlClick: boolean; //if disabled only the link button in the view header will open links
|
||||
@@ -133,6 +137,9 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
zoomToFitMaxLevel: 2,
|
||||
linkPrefix: "📍",
|
||||
urlPrefix: "🌐",
|
||||
parseTODO: false,
|
||||
todo: "☐",
|
||||
done: "🗹",
|
||||
hoverPreviewWithoutCTRL: false,
|
||||
linkOpacity: 1,
|
||||
openInAdjacentPane: false,
|
||||
@@ -605,7 +612,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.showLinkBrackets)
|
||||
.onChange(async (value) => {
|
||||
.onChange(value => {
|
||||
this.plugin.settings.showLinkBrackets = value;
|
||||
this.applySettingsUpdate(true);
|
||||
}),
|
||||
@@ -618,7 +625,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
text
|
||||
.setPlaceholder(t("INSERT_EMOJI"))
|
||||
.setValue(this.plugin.settings.linkPrefix)
|
||||
.onChange((value) => {
|
||||
.onChange(value => {
|
||||
this.plugin.settings.linkPrefix = value;
|
||||
this.applySettingsUpdate(true);
|
||||
}),
|
||||
@@ -631,12 +638,61 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
text
|
||||
.setPlaceholder(t("INSERT_EMOJI"))
|
||||
.setValue(this.plugin.settings.urlPrefix)
|
||||
.onChange(async (value) => {
|
||||
.onChange(value => {
|
||||
this.plugin.settings.urlPrefix = value;
|
||||
this.applySettingsUpdate(true);
|
||||
}),
|
||||
);
|
||||
|
||||
let todoPrefixSetting:TextComponent, donePrefixSetting:TextComponent;
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("PARSE_TODO_NAME"))
|
||||
.setDesc(fragWithHTML(t("PARSE_TODO_DESC")))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.parseTODO)
|
||||
.onChange(value => {
|
||||
this.plugin.settings.parseTODO = value;
|
||||
todoPrefixSetting.setDisabled(!value);
|
||||
donePrefixSetting.setDisabled(!value);
|
||||
this.applySettingsUpdate(true);
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("TODO_NAME"))
|
||||
.setDesc(fragWithHTML(t("TODO_DESC")))
|
||||
.addText((text) => {
|
||||
todoPrefixSetting = text;
|
||||
text
|
||||
.setPlaceholder(t("INSERT_EMOJI"))
|
||||
.setValue(this.plugin.settings.todo)
|
||||
.onChange(value => {
|
||||
this.plugin.settings.todo = value;
|
||||
this.applySettingsUpdate(true);
|
||||
})
|
||||
}
|
||||
);
|
||||
todoPrefixSetting.setDisabled(!this.plugin.settings.parseTODO);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(t("DONE_NAME"))
|
||||
.setDesc(fragWithHTML(t("DONE_DESC")))
|
||||
.setDisabled(!this.plugin.settings.parseTODO)
|
||||
.addText((text) => {
|
||||
donePrefixSetting = text;
|
||||
text
|
||||
.setPlaceholder(t("INSERT_EMOJI"))
|
||||
.setValue(this.plugin.settings.done)
|
||||
.onChange(value => {
|
||||
this.plugin.settings.done = value;
|
||||
this.applySettingsUpdate(true);
|
||||
})
|
||||
}
|
||||
);
|
||||
donePrefixSetting.setDisabled(!this.plugin.settings.parseTODO);
|
||||
|
||||
let opacityText: HTMLDivElement;
|
||||
new Setting(containerEl)
|
||||
.setName(t("LINKOPACITY_NAME"))
|
||||
|
||||
Reference in New Issue
Block a user