Skip to content

Commit

Permalink
1.0.6-test
Browse files Browse the repository at this point in the history
  • Loading branch information
zsviczian committed Apr 27, 2021
1 parent d64c00f commit 6e20735
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 52 deletions.
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "1.0.5",
"version": "1.0.6",
"minAppVersion": "0.11.13",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",
Expand Down
77 changes: 64 additions & 13 deletions src/ExcalidrawView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
EXCALIDRAW_FILE_EXTENSION,
ICON_NAME,
EXCALIDRAW_LIB_HEADER,
VIRGIL_FONT,
CASCADIA_FONT
} from './constants';
import ExcalidrawPlugin from './main';

Expand All @@ -40,26 +42,57 @@ export default class ExcalidrawView extends TextFileView {
this.justLoaded = false;
}

// get the new file content
getViewData () {
if(this.getScene) {
const scene = this.getScene();
if(this.plugin.settings.autoexportSVG) this.saveSVG(scene);
return scene;
public async saveSVG(data?: string) {
if(!data) {
if (!this.getScene) return false;
data = this.getScene();
}
else return this.data;
const filepath = this.file.path.substring(0,this.file.path.lastIndexOf('.'+EXCALIDRAW_FILE_EXTENSION)) + '.svg';
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
const exportSettings: ExportSettings = {
withBackground: this.plugin.settings.exportWithBackground,
withTheme: this.plugin.settings.exportWithTheme
}
const svg = ExcalidrawView.getSVG(data,exportSettings);
if(!svg) return;
//replace font references with base64 fonts
const includesVirgil = svg.querySelector("text[font-family^='Virgil']") != null;
const includesCascadia = svg.querySelector("text[font-family^='Cascadia']") != null;
const defs = svg.querySelector("defs");
if (defs && (includesCascadia || includesVirgil)) {
defs.innerHTML = "<style>" + (includesVirgil ? VIRGIL_FONT : "") + (includesCascadia ? CASCADIA_FONT : "")+"</style>";
}
const svgString = svg.outerHTML;
if(file && file instanceof TFile) await this.app.vault.modify(file,svgString);
else await this.app.vault.create(filepath,svgString);
}

private async saveSVG(data: string) {
const filepath = this.file.path.substring(0,this.file.path.lastIndexOf('.excalidraw')) + '.svg';
public async savePNG(data?: string) {
if(!data) {
if (!this.getScene) return false;
data = this.getScene();
}
const filepath = this.file.path.substring(0,this.file.path.lastIndexOf('.'+EXCALIDRAW_FILE_EXTENSION)) + '.png';
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
const exportSettings: ExportSettings = {
withBackground: this.plugin.settings.exportWithBackground,
withTheme: this.plugin.settings.exportWithTheme
}
const svg = ExcalidrawView.getSVG(data,exportSettings)?.outerHTML;
if(file && file instanceof TFile) await this.app.vault.modify(file,svg);
else await this.app.vault.create(filepath,svg);
const png = await ExcalidrawView.getPNG(data,exportSettings);
if(!png) return;
if(file && file instanceof TFile) await this.app.vault.modifyBinary(file,await png.arrayBuffer());
else await this.app.vault.createBinary(filepath,await png.arrayBuffer());
}

// get the new file content
getViewData () {
if(this.getScene) {
const scene = this.getScene();
if(this.plugin.settings.autoexportSVG) this.saveSVG(scene);
if(this.plugin.settings.autoexportPNG) this.savePNG(scene);
return scene;
}
else return this.data;
}

async onunload() {
Expand Down Expand Up @@ -208,7 +241,7 @@ export default class ExcalidrawView extends TextFileView {
},
onLibraryChange: async (items:LibraryItems) => {
this.plugin.settings.library = EXCALIDRAW_LIB_HEADER+JSON.stringify(items)+'}';
this.plugin.saveSettings();
await this.plugin.saveSettings();
}
})
)
Expand All @@ -233,4 +266,22 @@ export default class ExcalidrawView extends TextFileView {
return null;
}
}

public static async getPNG(data:string, exportSettings:ExportSettings) {
try {
const excalidrawData = JSON.parse(data);
return await Excalidraw.exportToBlob({
elements: excalidrawData.elements,
appState: {
exportBackground: exportSettings.withBackground,
exportWithDarkMode: exportSettings.withTheme ? (excalidrawData.appState?.theme=="light" ? false : true) : false,
... excalidrawData.appState,},
mimeType: "image/png",
exportWithDarkMode: "true",
metadata: "Generated by Excalidraw-Obsidian plugin",
});
} catch (error) {
return null;
}
}
}
4 changes: 2 additions & 2 deletions src/constants.ts

Large diffs are not rendered by default.

108 changes: 84 additions & 24 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,22 @@ export default class ExcalidrawPlugin extends Plugin {
})
}

const filename = source.match(/\[{2}(.*)\]{2}/m);
const fname = source.match(/\[{2}([^|]*).*\]{2}/m)[1];
const filenameWH = source.match(/\[{2}(.*)\|(\d*)x(\d*)\]{2}/m);
const filenameW = source.match(/\[{2}(.*)\|(\d*)\]{2}/m);

let fname:string = '';
let style = "excalidraw-svg"
style += source.contains("|left") ? "-left" : "";
style += source.contains("|right") ? "-right" : "";
style += source.contains("|center") ? "-center" : "";
let fwidth:string = this.settings.width;
let fheight:string = null;


if (filenameWH) {
fname = filenameWH[1];
fwidth = filenameWH[2];
fheight = filenameWH[3];
} else if (filenameW) {
fname = filenameW[1];
fwidth = filenameW[2];
} else if (filename) {
fname = filename[1];
}

if(fname == '') {
Expand Down Expand Up @@ -106,6 +105,7 @@ export default class ExcalidrawPlugin extends Plugin {
svg.removeAttribute('height');
svg.style.setProperty('width',fwidth);
if(fheight) svg.style.setProperty('height',fheight);
svg.addClass(style);
el.appendChild(svg);
});
});
Expand All @@ -115,49 +115,105 @@ export default class ExcalidrawPlugin extends Plugin {

this.openDialog = new OpenFileDialog(this.app, this);
this.addRibbonIcon(ICON_NAME, 'Excalidraw', async () => {
this.openDialog.start(openDialogAction.openFile);
this.createDrawing(this.getNextDefaultFilename(), this.settings.ribbonInNewPane);
});

this.addCommand({
id: "excalidraw-open",
name: "Open an existing drawing or create new one",
name: "Open an existing drawing or create new one on a new pane",
callback: () => {
this.openDialog.start(openDialogAction.openFile, true);
},
});

this.addCommand({
id: "excalidraw-open-on-current",
name: "Open an existing drawing or create new one on the currently active pane",
callback: () => {
this.openDialog.start(openDialogAction.openFile);
this.openDialog.start(openDialogAction.openFile, false);
},
});

this.addCommand({
id: "excalidraw-insert-transclusion",
name: "Transclude an ."+EXCALIDRAW_FILE_EXTENSION+" file into a markdown document",
callback: () => {
this.openDialog.start(openDialogAction.insertLink);
checkCallback: (checking: boolean) => {
if (checking) {
return this.app.workspace.activeLeaf.view.getViewType() == "markdown";
} else {
this.openDialog.start(openDialogAction.insertLink, false);
return true;
}
},
});


this.addCommand({
id: "excalidraw-autocreate",
name: "Create a new drawing",
name: "Create a new drawing and open on a new pane",
callback: () => {
this.createDrawing(this.getNextDefaultFilename(), true);
},
});

this.addCommand({
id: "excalidraw-autocreate-on-current",
name: "Create a new drawing and open on the currently active pane",
callback: () => {
this.createDrawing(this.getNextDefaultFilename());
this.createDrawing(this.getNextDefaultFilename(), false);
},
});

this.addCommand({
id: 'export-svg',
name: 'Export the current drawing as an SVG image next to the current .excalidraw file',
checkCallback: (checking: boolean) => {
if (checking) {
return this.app.workspace.activeLeaf.view.getViewType() == VIEW_TYPE_EXCALIDRAW;
} else {
const view = this.app.workspace.activeLeaf.view;
if(view.getViewType() == VIEW_TYPE_EXCALIDRAW) {
(this.app.workspace.activeLeaf.view as ExcalidrawView).saveSVG();
return true;
}
else return false;
}
},
});

this.addCommand({
id: 'export-png',
name: 'Export the current drawing as a PNG image next to the current .excalidraw file',
checkCallback: (checking: boolean) => {
if (checking) {
return this.app.workspace.activeLeaf.view.getViewType() == VIEW_TYPE_EXCALIDRAW;
} else {
const view = this.app.workspace.activeLeaf.view;
if(view.getViewType() == VIEW_TYPE_EXCALIDRAW) {
(this.app.workspace.activeLeaf.view as ExcalidrawView).savePNG();
return true;
}
else return false;
}
},
});
//watch filename change to rename .svg
this.app.vault.on('rename',async (file,oldPath) => {
if (!this.settings.keepInSync) return;
const oldSVGpath = oldPath.substring(0,oldPath.lastIndexOf('.excalidraw')) + '.svg';
if (!(this.settings.keepInSync && file instanceof TFile)) return;
if (file.extension != EXCALIDRAW_FILE_EXTENSION) return;
const oldSVGpath = oldPath.substring(0,oldPath.lastIndexOf('.'+EXCALIDRAW_FILE_EXTENSION)) + '.svg';
const svgFile = this.app.vault.getAbstractFileByPath(normalizePath(oldSVGpath));
if(svgFile && svgFile instanceof TFile) {
const newSVGpath = file.path.substring(0,file.path.lastIndexOf('.excalidraw')) + '.svg';
const newSVGpath = file.path.substring(0,file.path.lastIndexOf('.'+EXCALIDRAW_FILE_EXTENSION)) + '.svg';
await this.app.vault.rename(svgFile,newSVGpath);
}
});

//watch file delete and delete corresponding .svg
this.app.vault.on('delete',async (file) => {
if (!this.settings.keepInSync) return;
const svgPath = file.path.substring(0,file.path.lastIndexOf('.excalidraw')) + '.svg';
this.app.vault.on('delete',async (file:TFile) => {
if (!(this.settings.keepInSync && file instanceof TFile)) return;
if (file.extension != EXCALIDRAW_FILE_EXTENSION) return;
const svgPath = file.path.substring(0,file.path.lastIndexOf('.'+EXCALIDRAW_FILE_EXTENSION)) + '.svg';
const svgFile = this.app.vault.getAbstractFileByPath(normalizePath(svgPath));
if(svgFile && svgFile instanceof TFile) {
await this.app.vault.delete(svgFile);
Expand Down Expand Up @@ -187,7 +243,7 @@ export default class ExcalidrawPlugin extends Plugin {
await this.saveData(this.settings);
}

public async openDrawing(drawingFile: TFile) {
public async openDrawing(drawingFile: TFile, onNewPane: boolean) {
const leafs = this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
let leaf:WorkspaceLeaf = null;

Expand All @@ -201,6 +257,10 @@ export default class ExcalidrawPlugin extends Plugin {
if(!leaf) {
leaf = this.app.workspace.getLeaf();
}

if(onNewPane) {
leaf = this.app.workspace.createLeafBySplit(leaf);
}

leaf.setViewState({
type: VIEW_TYPE_EXCALIDRAW,
Expand All @@ -212,7 +272,7 @@ export default class ExcalidrawPlugin extends Plugin {
return this.settings.folder+'/Drawing ' + window.moment().format('YYYY-MM-DD HH.mm.ss')+'.'+EXCALIDRAW_FILE_EXTENSION;
}

public async createDrawing(filename: string) {
public async createDrawing(filename: string, onNewPane: boolean) {
const folder = this.app.vault.getAbstractFileByPath(normalizePath(this.settings.folder));
if (!(folder && folder instanceof TFolder)) {
await this.app.vault.createFolder(this.settings.folder);
Expand All @@ -221,9 +281,9 @@ export default class ExcalidrawPlugin extends Plugin {
const file = this.app.vault.getAbstractFileByPath(normalizePath(this.settings.templateFilePath));
if(file && file instanceof TFile) {
const content = await this.app.vault.read(file);
this.openDrawing(await this.app.vault.create(filename,content==''?BLANK_DRAWING:content));
this.openDrawing(await this.app.vault.create(filename,content==''?BLANK_DRAWING:content), onNewPane);
} else {
this.openDrawing(await this.app.vault.create(filename,BLANK_DRAWING));
this.openDrawing(await this.app.vault.create(filename,BLANK_DRAWING), onNewPane);
}
}
}
9 changes: 6 additions & 3 deletions src/openDrawing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
public app: App;
private plugin: ExcalidrawPlugin;
private action: openDialogAction;
private onNewPane: boolean;

constructor(app: App, plugin: ExcalidrawPlugin) {
super(app);
this.app = app;
this.action = openDialogAction.openFile;
this.plugin = plugin;
this.onNewPane = false;
this.setInstructions([{
command: "Type name of drawing to select.",
purpose: "",
Expand All @@ -32,7 +34,7 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
this.inputEl.onkeyup = (e) => {
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_FILE_EXTENSION);
this.plugin.createDrawing(this.plugin.settings.folder+'/'+this.inputEl.value+'.'+EXCALIDRAW_FILE_EXTENSION, this.onNewPane);
this.close();
}
}
Expand All @@ -51,16 +53,17 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
onChooseItem(item: TFile, _evt: MouseEvent | KeyboardEvent): void {
switch(this.action) {
case(openDialogAction.openFile):
this.plugin.openDrawing(item);
this.plugin.openDrawing(item, this.onNewPane);
break;
case(openDialogAction.insertLink):
this.plugin.insertCodeblock(item.path);
break;
}
}

start(action:openDialogAction): void {
start(action:openDialogAction, onNewPane: boolean): void {
this.action = action;
this.onNewPane = onNewPane;
switch(action) {
case (openDialogAction.openFile):
this.emptyStateText = EMPTY_MESSAGE;
Expand Down
Loading

0 comments on commit 6e20735

Please sign in to comment.