This commit is contained in:
Zsolt Viczian
2021-04-20 13:57:53 +02:00
parent fea67c100b
commit f2012de41c
5 changed files with 130 additions and 15 deletions

View File

@@ -1 +1 @@
{"folder":"excalidraw","templateFilePath":"","openFile":"excalidraw/new file.excalidraw","settings":{"folder":"excalidraw","templateFilePath":""}}
{"folder":"excalidraw","templateFilePath":"excalidraw/Template.excalidraw","openFile":"excalidraw/new file.excalidraw","settings":{"folder":"excalidraw","templateFilePath":""}}

View File

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

View File

@@ -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 = `<path fill="currentColor" stroke="currentColor" d="M52.6,0C40.5,0,28.8,3.8,20.2,11.6S6,31.4,6,47.4c0,26,20.2,52.6,48.8,52.6h0.4c0,0,0,0,0.1,0c5.9-0.1,11.6-2.1,15.8-5.9 c4.2-3.8,6.9-9.4,6.9-16.4c0-3.9-1.8-6.9-2.8-9.3c0,0,0,0,0-0.1c-1.4-3.5-0.9-5.1,0.4-6.8c1.3-1.8,4-3.5,7-5.7 c6.1-4.4,13.5-11.2,13.4-25.9c0-5.2-2.9-12.5-9.7-18.7C79.5,4.9,68.6,0,52.6,0L52.6,0z M52.6,4c15.2,0,25,4.6,31.1,10.1 c6.1,5.5,8.3,12.1,8.3,15.8c0.1,13.5-5.9,18.5-11.8,22.7c-2.9,2.1-5.8,3.9-7.8,6.6c-2,2.6-2.6,6.3-0.9,10.6c0,0,0,0,0,0.1 c1.1,2.8,2.4,5.4,2.4,7.9c0,6-2.2,10.4-5.6,13.5s-8.1,4.8-13.2,4.9h-0.4C28.7,96,10,71.3,10,47.4c0-15,5.1-25.7,12.9-32.8 S41.3,4,52.6,4z M53,10c-2.6,0-4.9,1.4-6.5,3.4c-1.6,2-2.5,4.7-2.5,7.6c0,2.9,0.9,5.5,2.5,7.6c1.6,2,3.9,3.4,6.5,3.4 c2.6,0,4.9-1.4,6.5-3.4c1.6-2,2.5-4.7,2.5-7.6s-0.9-5.5-2.5-7.6C57.9,11.4,55.6,10,53,10z M53,14c1.2,0,2.4,0.6,3.4,1.9 c1,1.2,1.6,3.1,1.6,5.1s-0.7,3.9-1.6,5.1c-1,1.2-2.1,1.9-3.4,1.9s-2.4-0.6-3.4-1.9c-1-1.2-1.6-3.1-1.6-5.1c0-2.1,0.7-3.9,1.6-5.1 C50.6,14.6,51.8,14,53,14z M31,20c-2.6,0-4.9,1.4-6.5,3.4c-1.6,2-2.5,4.7-2.5,7.6s0.9,5.5,2.5,7.6c1.6,2,3.9,3.4,6.5,3.4 s4.9-1.4,6.5-3.4c1.6-2,2.5-4.7,2.5-7.6s-0.9-5.5-2.5-7.6C35.9,21.4,33.6,20,31,20z M75,20c-2.6,0-4.9,1.4-6.5,3.4 c-1.6,2-2.5,4.7-2.5,7.6s0.9,5.5,2.5,7.6c1.6,2,3.9,3.4,6.5,3.4s4.9-1.4,6.5-3.4c1.6-2,2.5-4.7,2.5-7.6s-0.9-5.5-2.5-7.6 C79.9,21.4,77.6,20,75,20z M31,24c1.2,0,2.4,0.6,3.4,1.9S36,28.9,36,31s-0.7,3.9-1.6,5.1c-1,1.2-2.1,1.9-3.4,1.9 c-1.2,0-2.4-0.6-3.4-1.9c-1-1.2-1.6-3.1-1.6-5.1s0.7-3.9,1.6-5.1S29.8,24,31,24z M75,24c1.2,0,2.4,0.6,3.4,1.9 c1,1.2,1.6,3.1,1.6,5.1s-0.7,3.9-1.6,5.1c-1,1.2-2.1,1.9-3.4,1.9s-2.4-0.6-3.4-1.9c-1-1.2-1.6-3.1-1.6-5.1s0.7-3.9,1.6-5.1 S73.8,24,75,24z M29,46c-2.6,0-4.9,1.4-6.5,3.4c-1.6,2-2.5,4.7-2.5,7.6s0.9,5.5,2.5,7.6c1.6,2,3.9,3.4,6.5,3.4s4.9-1.4,6.5-3.4 S38,59.9,38,57s-0.9-5.5-2.5-7.6C33.9,47.4,31.6,46,29,46z M29,50c1.2,0,2.4,0.6,3.4,1.9c1,1.2,1.6,3.1,1.6,5.1s-0.7,3.9-1.6,5.1 c-1,1.2-2.1,1.9-3.4,1.9c-1.2,0-2.4-0.6-3.4-1.9c-1-1.2-1.6-3.1-1.6-5.1s0.7-3.9,1.6-5.1C26.6,50.6,27.8,50,29,50z M54,66 c-6.6,0-12,5.4-12,12s5.4,12,12,12s12-5.4,12-12S60.6,66,54,66z M54,70c4.6,0,8,3.4,8,8s-3.4,8-8,8s-8-3.4-8-8S49.4,70,54,70z"/>`;
export const PALETTE_ICON = `<path fill="currentColor" stroke="currentColor" d="M52.6,0C40.5,0,28.8,3.8,20.2,11.6S6,31.4,6,47.4c0,26,20.2,52.6,48.8,52.6h0.4c0,0,0,0,0.1,0c5.9-0.1,11.6-2.1,15.8-5.9 c4.2-3.8,6.9-9.4,6.9-16.4c0-3.9-1.8-6.9-2.8-9.3c0,0,0,0,0-0.1c-1.4-3.5-0.9-5.1,0.4-6.8c1.3-1.8,4-3.5,7-5.7 c6.1-4.4,13.5-11.2,13.4-25.9c0-5.2-2.9-12.5-9.7-18.7C79.5,4.9,68.6,0,52.6,0L52.6,0z M52.6,4c15.2,0,25,4.6,31.1,10.1 c6.1,5.5,8.3,12.1,8.3,15.8c0.1,13.5-5.9,18.5-11.8,22.7c-2.9,2.1-5.8,3.9-7.8,6.6c-2,2.6-2.6,6.3-0.9,10.6c0,0,0,0,0,0.1 c1.1,2.8,2.4,5.4,2.4,7.9c0,6-2.2,10.4-5.6,13.5s-8.1,4.8-13.2,4.9h-0.4C28.7,96,10,71.3,10,47.4c0-15,5.1-25.7,12.9-32.8 S41.3,4,52.6,4z M53,10c-2.6,0-4.9,1.4-6.5,3.4c-1.6,2-2.5,4.7-2.5,7.6c0,2.9,0.9,5.5,2.5,7.6c1.6,2,3.9,3.4,6.5,3.4 c2.6,0,4.9-1.4,6.5-3.4c1.6-2,2.5-4.7,2.5-7.6s-0.9-5.5-2.5-7.6C57.9,11.4,55.6,10,53,10z M53,14c1.2,0,2.4,0.6,3.4,1.9 c1,1.2,1.6,3.1,1.6,5.1s-0.7,3.9-1.6,5.1c-1,1.2-2.1,1.9-3.4,1.9s-2.4-0.6-3.4-1.9c-1-1.2-1.6-3.1-1.6-5.1c0-2.1,0.7-3.9,1.6-5.1 C50.6,14.6,51.8,14,53,14z M31,20c-2.6,0-4.9,1.4-6.5,3.4c-1.6,2-2.5,4.7-2.5,7.6s0.9,5.5,2.5,7.6c1.6,2,3.9,3.4,6.5,3.4 s4.9-1.4,6.5-3.4c1.6-2,2.5-4.7,2.5-7.6s-0.9-5.5-2.5-7.6C35.9,21.4,33.6,20,31,20z M75,20c-2.6,0-4.9,1.4-6.5,3.4 c-1.6,2-2.5,4.7-2.5,7.6s0.9,5.5,2.5,7.6c1.6,2,3.9,3.4,6.5,3.4s4.9-1.4,6.5-3.4c1.6-2,2.5-4.7,2.5-7.6s-0.9-5.5-2.5-7.6 C79.9,21.4,77.6,20,75,20z M31,24c1.2,0,2.4,0.6,3.4,1.9S36,28.9,36,31s-0.7,3.9-1.6,5.1c-1,1.2-2.1,1.9-3.4,1.9 c-1.2,0-2.4-0.6-3.4-1.9c-1-1.2-1.6-3.1-1.6-5.1s0.7-3.9,1.6-5.1S29.8,24,31,24z M75,24c1.2,0,2.4,0.6,3.4,1.9 c1,1.2,1.6,3.1,1.6,5.1s-0.7,3.9-1.6,5.1c-1,1.2-2.1,1.9-3.4,1.9s-2.4-0.6-3.4-1.9c-1-1.2-1.6-3.1-1.6-5.1s0.7-3.9,1.6-5.1 S73.8,24,75,24z M29,46c-2.6,0-4.9,1.4-6.5,3.4c-1.6,2-2.5,4.7-2.5,7.6s0.9,5.5,2.5,7.6c1.6,2,3.9,3.4,6.5,3.4s4.9-1.4,6.5-3.4 S38,59.9,38,57s-0.9-5.5-2.5-7.6C33.9,47.4,31.6,46,29,46z M29,50c1.2,0,2.4,0.6,3.4,1.9c1,1.2,1.6,3.1,1.6,5.1s-0.7,3.9-1.6,5.1 c-1,1.2-2.1,1.9-3.4,1.9c-1.2,0-2.4-0.6-3.4-1.9c-1-1.2-1.6-3.1-1.6-5.1s0.7-3.9,1.6-5.1C26.6,50.6,27.8,50,29,50z M54,66 c-6.6,0-12,5.4-12,12s5.4,12,12,12s12-5.4,12-12S60.6,66,54,66z M54,70c4.6,0,8,3.4,8,8s-3.4,8-8,8s-8-3.4-8-8S49.4,70,54,70z"/>`;
export const EMPTY_MESSAGE = "Hit enter to create a new drawing";

View File

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

View File

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