This commit is contained in:
Zsolt Viczian
2022-09-25 17:29:30 +02:00
parent ed316fc2c4
commit 71d00e0c8d
9 changed files with 182 additions and 14 deletions

View File

@@ -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",

View File

@@ -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

View File

@@ -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();

View File

@@ -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();

View File

@@ -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)
![image](https://user-images.githubusercontent.com/14358394/192145020-94bdd115-d24f-47c7-86fe-1417c53980c4.png)
<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

View File

@@ -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. " +

View File

@@ -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(

View File

@@ -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,
);
}}

View File

@@ -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"))