diff --git a/data.json b/data.json
index 0268b74..5bb7a5e 100644
--- a/data.json
+++ b/data.json
@@ -1 +1 @@
-{"folder":"excalidraw","templateFilePath":"","openFile":"excalidraw/new file.excalidraw","settings":{"folder":"excalidraw","templateFilePath":""}}
\ No newline at end of file
+{"folder":"excalidraw","templateFilePath":"excalidraw/Template.excalidraw","openFile":"excalidraw/new file.excalidraw","settings":{"folder":"excalidraw","templateFilePath":""}}
\ No newline at end of file
diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts
index 0501e51..16658a1 100644
--- a/src/ExcalidrawView.ts
+++ b/src/ExcalidrawView.ts
@@ -1,7 +1,7 @@
import { TextFileView, WorkspaceLeaf } from "obsidian";
import * as React from "react";
import * as ReactDOM from "react-dom";
-import Excalidraw from "@excalidraw/excalidraw";
+import Excalidraw, {exportToSvg} from "@excalidraw/excalidraw";
import { ExcalidrawElement } from "@excalidraw/excalidraw/types/element/types";
import { AppState } from "@excalidraw/excalidraw/types/types";
@@ -14,6 +14,12 @@ export default class ExcalidrawView extends TextFileView {
this.getScene = null;
}
+ async onClose() {
+ this.requestSave();
+ }
+
+
+
// clear the view content
clear() {
ReactDOM.unmountComponentAtNode(this.contentEl);
@@ -26,7 +32,17 @@ export default class ExcalidrawView extends TextFileView {
else return '';
}
+
+
setViewData (data: string, clear: boolean) {
+ if (this.app.workspace.layoutReady) {
+ this.loadDrawing(data,clear);
+ } else {
+ this.registerEvent(this.app.workspace.on('layout-ready', async () => this.loadDrawing(data,clear)));
+ }
+ }
+
+ private loadDrawing (data:string, clear:boolean) :void {
if(clear) this.clear();
const excalidrawData = JSON.parse(data);
this.instantiateExcalidraw({
@@ -84,7 +100,7 @@ export default class ExcalidrawView extends TextFileView {
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, [excalidrawWrapperRef]);
-
+
this.getScene = function() {
const el: ExcalidrawElement[] = excalidrawRef.current.getSceneElements();
const st: AppState = excalidrawRef.current.getAppState();
@@ -128,4 +144,21 @@ export default class ExcalidrawView extends TextFileView {
);
}),(this as any).contentEl);
}
+
+ public static getSVG(data:string):SVGSVGElement {
+ try {
+ const excalidrawData = JSON.parse(data);
+ return exportToSvg({
+ elements: excalidrawData.elements,
+ appState: {
+ exportBackground: true,
+ exportWithDarkMode: excalidrawData.appState?.theme=="light" ? false : true,
+ ... excalidrawData.appState,},
+ exportPadding:10,
+ metadata: "Generated by Excalidraw-Obsidian plugin",
+ });
+ } catch (error) {
+ return null;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/constants.ts b/src/constants.ts
index a3895a7..76eb832 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -2,4 +2,5 @@ export const VIEW_TYPE_EXCALIDRAW = "excalidraw";
export const MAX_COLORS = 5;
export const COLOR_FREQ = 6;
export const BLANK_DRAWING = '{"type":"excalidraw","version":2,"source":"https://excalidraw.com","elements":[],"appState":{"gridSize":null,"viewBackgroundColor":"#ffffff"}}';
-export const PALETTE_ICON = ``;
\ No newline at end of file
+export const PALETTE_ICON = ``;
+export const EMPTY_MESSAGE = "Hit enter to create a new drawing";
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index 9128408..16f6d3e 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -8,7 +8,10 @@ import {
PluginManifest,
EventRef,
Menu,
- TAbstractFile
+ TAbstractFile,
+ MarkdownPostProcessorContext,
+ MarkdownView,
+ Editor,
} from 'obsidian';
import { BLANK_DRAWING, VIEW_TYPE_EXCALIDRAW, PALETTE_ICON } from './constants';
import ExcalidrawView from './ExcalidrawView';
@@ -17,8 +20,9 @@ import {
DEFAULT_SETTINGS,
ExcalidrawSettingTab
} from './settings';
-import {OpenFileDialog} from './openDrawing';
+import {openDialogAction, OpenFileDialog} from './openDrawing';
import {getDateString} from './utils'
+import { exportToSvg } from '@excalidraw/excalidraw';
export default class ExcalidrawPlugin extends Plugin {
@@ -45,22 +49,59 @@ export default class ExcalidrawPlugin extends Plugin {
this.registerExtensions(["excalidraw"],"excalidraw");
+ this.registerMarkdownCodeBlockProcessor('excalidraw', (source,el,ctx) => {
+ const parseError = (message: string) => {
+ el.createDiv("excalidraw-error",(el)=> {
+ el.createEl("p","Please provide a link to an excalidraw file: [[file.excalidraw]]");
+ el.createEl("p",message);
+ el.createEl("p",source);
+ })
+ }
+
+ const filename = source.match(/\[{2}(.*)\]{2}/m);
+ if(filename.length==2) {
+ const file:TFile = (this.app.vault.getAbstractFileByPath(filename[1]) as TFile);
+ if(file) {
+ if(file.extension == "excalidraw") {
+ this.app.vault.read(file).then(async (content: string) => {
+ const svg = ExcalidrawView.getSVG(content);
+ if(svg) {
+ el.createDiv("excalidraw-svg",(el)=> {
+ el.appendChild(svg);
+ })
+
+ } else parseError("Parse error. Not a valid Excalidraw file.");
+ });
+ } else parseError("Not an excalidraw file. Must have extension .excalidraw");
+ } else parseError("File does not exist");
+ } else parseError("No link to file found in codeblock.");
+ });
+
await this.loadSettings();
this.addSettingTab(new ExcalidrawSettingTab(this.app, this));
this.openDialog = new OpenFileDialog(this.app, this);
this.addRibbonIcon('excalidraw', 'Excalidraw', async () => {
- this.openDialog.start();
+ this.openDialog.start(openDialogAction.openFile);
});
this.addCommand({
id: "excalidraw-open",
name: "Open existing drawing or create new one",
callback: () => {
- this.openDialog.start();
+ this.openDialog.start(openDialogAction.openFile);
},
});
+ this.addCommand({
+ id: "excalidraw-insert-transclusion",
+ name: "Insert link to .excalidraw file into markdown document",
+ callback: () => {
+ this.openDialog.start(openDialogAction.insertLink);
+ },
+ });
+
+
this.addCommand({
id: "excalidraw-autocreate",
name: "Create a new drawing",
@@ -69,6 +110,20 @@ export default class ExcalidrawPlugin extends Plugin {
},
});
}
+
+ public insertCodeblock(data:string) {
+ const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
+ if(activeView) {
+ const editor = activeView.editor;
+ let doc = editor.getDoc();
+ doc.replaceSelection(
+ String.fromCharCode(96,96,96) +
+ "excalidraw\n[["+data+"]]\n" +
+ String.fromCharCode(96,96,96));
+ editor.focus();
+ }
+
+ }
private async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
diff --git a/src/openDrawing.ts b/src/openDrawing.ts
index 4b0cf86..cf132e7 100644
--- a/src/openDrawing.ts
+++ b/src/openDrawing.ts
@@ -1,24 +1,32 @@
import { App, FuzzySuggestModal, TFile, TFolder, normalizePath, Vault, TAbstractFile, Instruction } from "obsidian";
import ExcalidrawPlugin from './main';
import ExcalidrawView from './view';
+import {EMPTY_MESSAGE} from './constants';
+
+export enum openDialogAction {
+ openFile,
+ insertLink,
+}
export class OpenFileDialog extends FuzzySuggestModal {
public app: App;
private plugin: ExcalidrawPlugin;
+ private action: openDialogAction;
+
constructor(app: App, plugin: ExcalidrawPlugin) {
super(app);
this.app = app;
+ this.action = openDialogAction.openFile;
this.plugin = plugin;
- const EMPTY_MESSAGE = "Hit enter to create a new drawing";
- this.emptyStateText = EMPTY_MESSAGE;
this.setInstructions([{
- command: "Select an existing drawing or type title for your new drawing, then hit enter.",
- purpose: "The new drawing will be created in the default Excalidraw folder specified in Settings.",
+ command: "Type name of drawing to select.",
+ purpose: "",
}]);
+
this.inputEl.onkeyup = (e) => {
- if(e.key=="Enter") {
+ if(e.key=="Enter" && this.action == openDialogAction.openFile) {
if (this.containerEl.innerText.includes(EMPTY_MESSAGE)) {
this.plugin.createDrawing(this.plugin.settings.folder+'/'+this.inputEl.value+'.excalidraw');
this.close();
@@ -38,10 +46,28 @@ export class OpenFileDialog extends FuzzySuggestModal {
}
onChooseItem(item: TFile, _evt: MouseEvent | KeyboardEvent): void {
- this.plugin.openDrawing(item);
+ switch(this.action) {
+ case(openDialogAction.openFile):
+ this.plugin.openDrawing(item);
+ break;
+ case(openDialogAction.insertLink):
+ this.plugin.insertCodeblock(item.path);
+ break;
+ }
}
- start(): void {
+ start(action:openDialogAction): void {
+ this.action = action;
+ switch(action) {
+ case (openDialogAction.openFile):
+ this.emptyStateText = EMPTY_MESSAGE;
+ this.setPlaceholder("Select existing drawing or type name of new and hit enter.");
+ break;
+ case (openDialogAction.insertLink):
+ this.emptyStateText = "No file matches your query.";
+ this.setPlaceholder("Select existing drawing to insert into document.");
+ break;
+ }
try {
let files = this.getItems();
this.open();