Compare commits

..

2 Commits

Author SHA1 Message Date
Zsolt Viczian
8834762004 1.6.27 2022-05-01 20:30:56 +02:00
Zsolt Viczian
48477c7ee9 onload-script, isExcalidrawView 2022-04-30 20:29:14 +02:00
13 changed files with 178 additions and 43 deletions

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "obsidian-excalidraw-plugin",
"version": "1.6.26",
"version": "1.6.27",
"description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@@ -376,6 +376,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
exportSettings?: ExportSettings,
loader?: EmbeddedFilesLoader,
theme?: string,
padding?: number,
): Promise<SVGSVGElement> {
if (!theme) {
theme = this.plugin.settings.previewMatchObsidianTheme
@@ -409,7 +410,8 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
this.canvas.viewBackgroundColor,
this.getElements(),
this.plugin,
0
0,
padding
);
};
@@ -1327,22 +1329,42 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
* Register instance of EA to use for hooks with TargetView
* By default ExcalidrawViews will check window.ExcalidrawAutomate for event hooks.
* Using this event you can set a different instance of Excalidraw Automate for hooks
* @returns true if successful
*/
registerThisAsViewEA() {
registerThisAsViewEA():boolean {
//@ts-ignore
if (!this.targetView || !this.targetView?._loaded) {
errorMessage("targetView not set", "addElementsToView()");
return false;
}
this.targetView.hookServer = this;
this.targetView.setHookServer(this);
return true;
}
/**
* Sets the targetView EA to window.ExcalidrawAutomate
* @returns true if successful
*/
deregisterThisAsViewEA():boolean {
//@ts-ignore
if (!this.targetView || !this.targetView?._loaded) {
errorMessage("targetView not set", "addElementsToView()");
return false;
}
this.targetView.setHookServer(this);
return true;
}
/**
* If set, this callback is triggered when the user closes an Excalidraw view.
*/
onViewUnloadHook: (view: ExcalidrawView) => void = null;
/**
* If set, this callback is triggered, when the user changes the view mode.
* You can use this callback in case you want to do something additional when the user switches to view mode and back.
*/
onViewModeChangeHook: (isViewModeEnabled:boolean) => void = null;
onViewModeChangeHook: (isViewModeEnabled:boolean, view: ExcalidrawView, ea: ExcalidrawAutomate) => void = null;
/**
* If set, this callback is triggered, when the user hovers a link in the scene.
@@ -1353,6 +1375,8 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
onLinkHoverHook: (
element: NonDeletedExcalidrawElement,
linkText: string,
view: ExcalidrawView,
ea: ExcalidrawAutomate
) => boolean = null;
/**
@@ -1364,7 +1388,9 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
onLinkClickHook:(
element: ExcalidrawElement,
linkText: string,
event: MouseEvent
event: MouseEvent,
view: ExcalidrawView,
ea: ExcalidrawAutomate
) => boolean = null;
/**
@@ -1554,6 +1580,15 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
return manifest.version >= requiredVersion;
};
/**
* Check if view is instance of ExcalidrawView
* @param view
* @returns
*/
isExcalidrawView(view: any): boolean {
return view instanceof ExcalidrawView;
}
/**
* sets selection in view
* @param elements

View File

@@ -15,6 +15,7 @@ import {
fileid,
REG_BLOCK_REF_CLEAN,
FRONTMATTER_KEY_LINKBUTTON_OPACITY,
FRONTMATTER_KEY_ONLOAD_SCRIPT,
} from "./Constants";
import { _measureText } from "./ExcalidrawAutomate";
import ExcalidrawPlugin from "./main";
@@ -1267,6 +1268,17 @@ export class ExcalidrawData {
return opacity;
}
public getOnLoadScript(): string {
const fileCache = this.app.metadataCache.getFileCache(this.file);
if (
fileCache?.frontmatter &&
fileCache.frontmatter[FRONTMATTER_KEY_ONLOAD_SCRIPT] != null
) {
return fileCache.frontmatter[FRONTMATTER_KEY_ONLOAD_SCRIPT];
}
return null;
}
private setLinkPrefix(): boolean {
const linkPrefix = this.linkPrefix;
const fileCache = this.app.metadataCache.getFileCache(this.file);

View File

@@ -180,7 +180,7 @@ export default class ExcalidrawView extends TextFileView {
public toolsPanelRef: React.MutableRefObject<any> = null;
private parentMoveObserver: MutationObserver;
public linksAlwaysOpenInANewPane: boolean = false; //override the need for SHIFT+CTRL+click
public hookServer: ExcalidrawAutomate;
private hookServer: ExcalidrawAutomate;
public semaphores: {
//The role of justLoaded is to capture the Excalidraw.onChange event that fires right after the canvas was loaded for the first time to
@@ -246,6 +246,16 @@ export default class ExcalidrawView extends TextFileView {
this.hookServer = plugin.ea;
}
setHookServer(ea:ExcalidrawAutomate) {
if(ea) {
this.hookServer = ea;
} else {
this.hookServer = this.plugin.ea;
}
}
getHookServer = () => this.hookServer ?? this.plugin.ea;
preventAutozoom() {
this.semaphores.preventAutozoom = true;
setTimeout(() => (this.semaphores.preventAutozoom = false), 2000);
@@ -643,15 +653,21 @@ export default class ExcalidrawView extends TextFileView {
}
linkText = linkText.replaceAll("\n", ""); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/187
if(this.hookServer?.onLinkClickHook) {
if(this.getHookServer().onLinkClickHook) {
const id = selectedText.id??selectedElementWithLink.id;
const el = this.excalidrawAPI.getSceneElements().filter((el:ExcalidrawElement)=>el.id === id)[0];
try {
if(!this.hookServer.onLinkClickHook(el,linkText,ev)) {
if(!this.getHookServer().onLinkClickHook(
el,
linkText,
ev,
this,
this.getHookServer()
)) {
return;
}
} catch (e) {
errorlog({where: "ExcalidrawView.handleLinkClick selectedText.id!==null", fn: this.hookServer.onLinkClickHook, error: e});
errorlog({where: "ExcalidrawView.handleLinkClick selectedText.id!==null", fn: this.getHookServer().onLinkClickHook, error: e});
}
}
@@ -768,15 +784,21 @@ export default class ExcalidrawView extends TextFileView {
return;
}
if(this.hookServer?.onLinkClickHook) {
if(this.getHookServer().onLinkClickHook) {
const id = selectedImage.id??selectedText.id??selectedElementWithLink.id;
const el = this.excalidrawAPI.getSceneElements().filter((el:ExcalidrawElement)=>el.id === id)[0];
try {
if(!this.hookServer.onLinkClickHook(el,linkText,ev)) {
if(!this.getHookServer().onLinkClickHook(
el,
linkText,
ev,
this,
this.getHookServer()
)) {
return;
}
} catch (e) {
errorlog({where: "ExcalidrawView.handleLinkClick selectedText.id===null", fn: this.hookServer.onLinkClickHook, error: e});
errorlog({where: "ExcalidrawView.handleLinkClick selectedText.id===null", fn: this.getHookServer().onLinkClickHook, error: e});
}
}
@@ -1041,6 +1063,13 @@ export default class ExcalidrawView extends TextFileView {
//save current drawing when user closes workspace leaf
async onunload() {
if(this.getHookServer().onViewUnloadHook) {
try {
this.getHookServer().onViewUnloadHook(this);
} catch(e) {
errorlog({where: "ExcalidrawView.onunload", fn: this.getHookServer().onViewUnloadHook, error: e});
}
}
this.removeParentMoveObserver();
this.removeSlidingPanesListner();
const tooltip = document.body.querySelector(
@@ -1234,6 +1263,19 @@ export default class ExcalidrawView extends TextFileView {
}
}
await this.loadDrawing(true);
const script = this.excalidrawData.getOnLoadScript();
if(script) {
const self = this;
const scriptname = this.file.basename+ "-onlaod-script";
const runScript = () => {
if(!self.excalidrawAPI) { //need to wait for Excalidraw to initialize
setTimeout(runScript,200);
return;
}
self.plugin.scriptEngine.executeScript(self,script,scriptname);
}
runScript();
}
this.isLoaded = true;
});
}
@@ -2103,13 +2145,18 @@ export default class ExcalidrawView extends TextFileView {
}
}
if(this.hookServer?.onLinkHoverHook) {
if(this.getHookServer().onLinkHoverHook) {
try {
if(!this.hookServer.onLinkHoverHook(element,linktext)) {
if(!this.getHookServer().onLinkHoverHook(
element,
linktext,
this,
this.getHookServer()
)) {
return;
}
} catch (e) {
errorlog({where: "ExcalidrawView.showHoverPreview", fn: this.hookServer.onLinkHoverHook, error: e});
errorlog({where: "ExcalidrawView.showHoverPreview", fn: this.getHookServer().onLinkHoverHook, error: e});
}
}
@@ -2352,11 +2399,11 @@ export default class ExcalidrawView extends TextFileView {
files: TFile[],
text: string,
): boolean => {
if (this.hookServer.onDropHook) {
if (this.getHookServer().onDropHook) {
try {
return this.hookServer.onDropHook({
return this.getHookServer().onDropHook({
//@ts-ignore
ea: this.hookServer, //the ExcalidrawAutomate object
ea: this.getHookServer(), //the ExcalidrawAutomate object
event, //React.DragEvent<HTMLDivElement>
draggable, //Obsidian draggable object
type, //"file"|"text"
@@ -2612,13 +2659,19 @@ export default class ExcalidrawView extends TextFileView {
return;
}
const event = e?.detail?.nativeEvent;
if(this.hookServer?.onLinkClickHook) {
if(this.getHookServer().onLinkClickHook) {
try {
if(!this.hookServer.onLinkClickHook(element,element.link,event)) {
if(!this.getHookServer().onLinkClickHook(
element,
element.link,
event,
this,
this.getHookServer()
)) {
return;
}
} catch (e) {
errorlog({where: "ExcalidrawView.onLinkOpen", fn: this.hookServer.onLinkClickHook, error: e});
errorlog({where: "ExcalidrawView.onLinkOpen", fn: this.getHookServer().onLinkClickHook, error: e});
}
}
if (link.startsWith(LOCAL_PROTOCOL) || link.startsWith("[[")) {
@@ -2719,11 +2772,11 @@ export default class ExcalidrawView extends TextFileView {
this.toolsPanelRef?.current?.setExcalidrawViewMode(
isViewModeEnabled,
);
if(this.hookServer?.onViewModeChangeHook) {
if(this.getHookServer().onViewModeChangeHook) {
try {
this.hookServer.onViewModeChangeHook(isViewModeEnabled);
this.getHookServer().onViewModeChangeHook(isViewModeEnabled,this,this.getHookServer());
} catch(e) {
errorlog({where: "ExcalidrawView.onViewModeChange", fn: this.hookServer.onViewModeChangeHook, error: e});
errorlog({where: "ExcalidrawView.onViewModeChange", fn: this.getHookServer().onViewModeChangeHook, error: e});
}
}

View File

@@ -170,7 +170,12 @@ export class ScriptEngine {
}
const view = this.plugin.app.workspace.activeLeaf.view;
if (view instanceof ExcalidrawView) {
this.executeScript(view, f);
(async()=>{
const script = await this.plugin.app.vault.read(f);
if(script) {
this.executeScript(view, script, scriptName);
}
})()
return true;
}
return false;
@@ -206,18 +211,13 @@ export class ScriptEngine {
delete app.commands.commands[commandId];
}
async executeScript(view: ExcalidrawView, f: TFile) {
if (!view || !f) {
async executeScript(view: ExcalidrawView, script: string, title: string) {
if (!view || !script || !title) {
return;
}
this.plugin.ea.reset();
this.plugin.ea.setView(view);
const script = await this.plugin.app.vault.read(f);
if (!script) {
return;
}
this.plugin.ea.activeScript = this.getScriptName(f);
this.plugin.ea.activeScript = title;
//https://stackoverflow.com/questions/45381204/get-asyncfunction-constructor-in-typescript changed tsconfig to es2017
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncFunction

View File

@@ -30,6 +30,7 @@ export const FRONTMATTER_KEY_EXPORT_PNGSCALE = "excalidraw-export-pngscale";
export const FRONTMATTER_KEY_CUSTOM_PREFIX = "excalidraw-link-prefix";
export const FRONTMATTER_KEY_CUSTOM_URL_PREFIX = "excalidraw-url-prefix";
export const FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS = "excalidraw-link-brackets";
export const FRONTMATTER_KEY_ONLOAD_SCRIPT = "excalidraw-onload-script";
export const FRONTMATTER_KEY_LINKBUTTON_OPACITY = "excalidraw-linkbutton-opacity";
export const FRONTMATTER_KEY_DEFAULT_MODE = "excalidraw-default-mode";
export const FRONTMATTER_KEY_FONT = "excalidraw-font";

View File

@@ -17,6 +17,10 @@ I develop this plugin as a hobby, spending most of my free time doing this. If y
<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.6.27": `
## New Features
- While these new features are benefitial for all Excalidraw Automation projects, the current changes are mainly in support of the [ExcaliBrain](https://youtu.be/O2s-h5VKCas) integration. See detailed [Release Notes](https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/1.6.27) on GitHub.
`,
"1.6.26": `
## Fixed
- Dragging multiple files onto the canvas will now correctly [#589](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/589)

View File

@@ -532,6 +532,12 @@ export const FRONTMATTER_KEYS_INFO: SuggesterInfo[] = [
"Valid values are between 0 and 1, where 0 means the button is transparent.",
after: ": 0.5",
},
{
field: "onload-script",
code: null,
desc: "The value of this field will be executed as javascript code using the Script Engine environment. Use this to initiate custom actions or logic when loading your drawing.",
after: ': "new Notice(`Hello World!\\n\\nFile: ${ea.targetView.file.basename}`);"',
},
{
field: "font",
code: null,

View File

@@ -1,9 +1,13 @@
import "obsidian";
import { ExcalidrawAutomate } from "./ExcalidrawAutomate";
import ExcalidrawView from "./ExcalidrawView";
//import { ExcalidrawAutomate } from "./ExcalidrawAutomate";
export {ExcalidrawAutomateInterface} from "./types";
export type { ExcalidrawBindableElement, ExcalidrawElement, FileId, FillStyle, StrokeSharpness, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
export type { Point } from "@zsviczian/excalidraw/types/types";
export const getEA = (view?:ExcalidrawView): ExcalidrawAutomate => {
return window.ExcalidrawAutomate.getAPI(view);
export const getEA = (view?:any): any => {
try {
return window.ExcalidrawAutomate.getAPI(view);
} catch(e) {
console.log({message: "Excalidraw not available", fn: getEA});
return null;
}
}

View File

@@ -520,13 +520,14 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
)
: this.state.scriptIconMap[key].name
}
action={() => {
action={async () => {
const f =
this.props.view.app.vault.getAbstractFileByPath(key);
if (f && f instanceof TFile) {
this.props.view.plugin.scriptEngine.executeScript(
this.props.view,
f,
await this.props.view.plugin.app.vault.read(f),
this.props.view.plugin.scriptEngine.getScriptName(f)
);
}
}}

21
src/types.d.ts vendored
View File

@@ -1,4 +1,4 @@
import { ExcalidrawBindableElement, ExcalidrawElement, FileId, FillStyle, StrokeSharpness, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
import { ExcalidrawBindableElement, ExcalidrawElement, FileId, FillStyle, NonDeletedExcalidrawElement, StrokeSharpness, StrokeStyle } from "@zsviczian/excalidraw/types/element/types";
import { Point } from "@zsviczian/excalidraw/types/types";
import { TFile, WorkspaceLeaf } from "obsidian";
import { EmbeddedFilesLoader } from "./EmbeddedFileLoader";
@@ -63,6 +63,7 @@ export interface ExcalidrawAutomateInterface {
exportSettings?: ExportSettings, //use ExcalidrawAutomate.getExportSettings(boolean,boolean)
loader?: EmbeddedFilesLoader, //use ExcalidrawAutomate.getEmbeddedFilesLoader(boolean?)
theme?: string,
padding?: number
): Promise<SVGSVGElement>;
createPNG(
templatePath?: string,
@@ -148,6 +149,23 @@ export interface ExcalidrawAutomateInterface {
//position in the stack of elements in the view even if modified using EA
newElementsOnTop?: boolean, //default is false, i.e. the new elements get to the bottom of the stack
): Promise<boolean>;
registerThisAsViewEA():boolean;
deregisterThisAsViewEA():boolean;
onViewUnloadHook(view: ExcalidrawView): void;
onViewModeChangeHook(isViewModeEnabled:boolean, view: ExcalidrawView, ea: ExcalidrawAutomate): void;
onLinkHoverHook(
element: NonDeletedExcalidrawElement,
linkText: string,
view: ExcalidrawView,
ea: ExcalidrawAutomate
):boolean;
onLinkClickHook(
element: ExcalidrawElement,
linkText: string,
event: MouseEvent,
view: ExcalidrawView,
ea: ExcalidrawAutomate
): boolean;
onDropHook(data: {
//if set Excalidraw will call this function onDrop events
ea: ExcalidrawAutomate;
@@ -198,6 +216,7 @@ export interface ExcalidrawAutomateInterface {
//recommended use:
//if(!ea.verifyMinimumPluginVersion || !ea.verifyMinimumPluginVersion("1.5.20")) {new Notice("message");return;}
verifyMinimumPluginVersion(requiredVersion: string): boolean;
isExcalidrawView(view: any): boolean;
selectElementsInView(elements: ExcalidrawElement[]): void; //sets selection in view
generateElementId(): string; //returns an 8 character long random id
cloneElement(element: ExcalidrawElement): ExcalidrawElement; //Returns a clone of the element with a new id

View File

@@ -1,4 +1,4 @@
{
"1.6.26": "0.12.16",
"1.6.27": "0.12.16",
"1.4.2": "0.11.13"
}