embed Excalidraw into document

This commit is contained in:
Zsolt Viczian
2021-10-04 18:22:47 +02:00
parent 78fb37b173
commit f785d756be
8 changed files with 750 additions and 688 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "obsidian-excalidraw-plugin",
"version": "1.1.10",
"version": "1.3.15",
"description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings",
"main": "main.js",
"scripts": {
@@ -11,7 +11,7 @@
"author": "",
"license": "MIT",
"dependencies": {
"@zsviczian/excalidraw": "0.9.0-obsidian-image-support-3",
"@zsviczian/excalidraw": "0.9.0-obsidian-image-support-5",
"monkey-around": "^2.2.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@@ -19,22 +19,22 @@
"roughjs": "4.4.1"
},
"devDependencies": {
"@babel/core": "^7.14.6",
"@babel/preset-env": "^7.3.1",
"@babel/core": "^7.15.5",
"@babel/preset-env": "^7.15.6",
"@babel/preset-react": "^7.14.5",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^15.1.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"@rollup/plugin-commonjs": "^21.0.0",
"@rollup/plugin-node-resolve": "^13.0.5",
"@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.1",
"@rollup/plugin-typescript": "^8.2.5",
"@types/node": "^15.12.4",
"@types/react-dom": "^17.0.8",
"@types/react-dom": "^17.0.9",
"cross-env": "^7.0.3",
"nanoid": "^3.1.23",
"obsidian": "^0.12.16",
"rollup": "^2.52.3",
"rollup-plugin-visualizer": "^5.5.0",
"tslib": "^2.3.0",
"typescript": "^4.3.4"
"rollup-plugin-visualizer": "^5.5.2",
"tslib": "^2.3.1",
"typescript": "^4.4.3"
}
}

View File

@@ -73,7 +73,7 @@ export interface ExcalidrawAutomate extends Window {
}
}
):Promise<string>;
createSVG (templatePath?:string):Promise<SVGSVGElement>;
createSVG (templatePath?:string, embedFont?:boolean):Promise<SVGSVGElement>;
createPNG (templatePath?:string):Promise<any>;
wrapText (text:string, lineLen:number):string;
addRect (topX:number, topY:number, width:number, height:number):string;
@@ -312,47 +312,50 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
source: "https://excalidraw.com",
elements: elements,
appState: {
theme: template ? template.appState.theme : this.canvas.theme,
viewBackgroundColor: template? template.appState.viewBackgroundColor : this.canvas.viewBackgroundColor,
currentItemStrokeColor: template? template.appState.currentItemStrokeColor : this.style.strokeColor,
currentItemBackgroundColor: template? template.appState.currentItemBackgroundColor : this.style.backgroundColor,
currentItemFillStyle: template? template.appState.currentItemFillStyle : this.style.fillStyle,
currentItemStrokeWidth: template? template.appState.currentItemStrokeWidth : this.style.strokeWidth,
currentItemStrokeStyle: template? template.appState.currentItemStrokeStyle : this.style.strokeStyle,
currentItemRoughness: template? template.appState.currentItemRoughness : this.style.roughness,
currentItemOpacity: template? template.appState.currentItemOpacity : this.style.opacity,
currentItemFontFamily: template? template.appState.currentItemFontFamily : this.style.fontFamily,
currentItemFontSize: template? template.appState.currentItemFontSize : this.style.fontSize,
currentItemTextAlign: template? template.appState.currentItemTextAlign : this.style.textAlign,
currentItemStrokeSharpness: template? template.appState.currentItemStrokeSharpness : this.style.strokeSharpness,
currentItemStartArrowhead: template? template.appState.currentItemStartArrowhead: this.style.startArrowHead,
currentItemEndArrowhead: template? template.appState.currentItemEndArrowhead : this.style.endArrowHead,
currentItemLinearStrokeSharpness: template? template.appState.currentItemLinearStrokeSharpness : this.style.strokeSharpness,
gridSize: template ? template.appState.gridSize : this.canvas.gridSize
theme: template?.appState?.theme ?? this.canvas.theme,
viewBackgroundColor: template?.appState?.viewBackgroundColor ?? this.canvas.viewBackgroundColor,
currentItemStrokeColor: template?.appState?.currentItemStrokeColor ?? this.style.strokeColor,
currentItemBackgroundColor: template?.appState?.currentItemBackgroundColor ?? this.style.backgroundColor,
currentItemFillStyle: template?.appState?.currentItemFillStyle ?? this.style.fillStyle,
currentItemStrokeWidth: template?.appState?.currentItemStrokeWidth ?? this.style.strokeWidth,
currentItemStrokeStyle: template?.appState?.currentItemStrokeStyle ?? this.style.strokeStyle,
currentItemRoughness: template?.appState?.currentItemRoughness ?? this.style.roughness,
currentItemOpacity: template?.appState?.currentItemOpacity ?? this.style.opacity,
currentItemFontFamily: template?.appState?.currentItemFontFamily ?? this.style.fontFamily,
currentItemFontSize: template?.appState?.currentItemFontSize ?? this.style.fontSize,
currentItemTextAlign: template?.appState?.currentItemTextAlign ?? this.style.textAlign,
currentItemStrokeSharpness: template?.appState?.currentItemStrokeSharpness ?? this.style.strokeSharpness,
currentItemStartArrowhead: template?.appState?.currentItemStartArrowhead ?? this.style.startArrowHead,
currentItemEndArrowhead: template?.appState?.currentItemEndArrowhead ?? this.style.endArrowHead,
currentItemLinearStrokeSharpness: template?.appState?.currentItemLinearStrokeSharpness ?? this.style.strokeSharpness,
gridSize: template?.appState?.gridSize ?? this.canvas.gridSize,
files: template?.appState?.files ?? {},
}
}))
);
},
async createSVG(templatePath?:string):Promise<SVGSVGElement> {
async createSVG(templatePath?:string,embedFont:boolean = false):Promise<SVGSVGElement> {
const template = templatePath ? (await getTemplate(templatePath)) : null;
let elements = template ? template.elements : [];
elements = elements.concat(this.getElements());
return await ExcalidrawView.getSVG(
const svg = await ExcalidrawView.getSVG(
{//createDrawing
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": elements,
"appState": {
"theme": template ? template.appState.theme : this.canvas.theme,
"viewBackgroundColor": template? template.appState.viewBackgroundColor : this.canvas.viewBackgroundColor
type: "excalidraw",
version: 2,
source: "https://excalidraw.com",
elements: elements,
appState: {
theme: template?.appState?.theme ?? this.canvas.theme,
viewBackgroundColor: template?.appState?.viewBackgroundColor ?? this.canvas.viewBackgroundColor,
files: template?.appState?.files ?? {}
}
},//),
},
{
withBackground: plugin.settings.exportWithBackground,
withTheme: plugin.settings.exportWithTheme
}
)
)
return embedFont ? ExcalidrawView.embedFontsInSVG(svg) : svg;
},
async createPNG(templatePath?:string, scale:number=1) {
const template = templatePath ? (await getTemplate(templatePath)) : null;
@@ -360,13 +363,14 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
elements = elements.concat(this.getElements());
return ExcalidrawView.getPNG(
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": elements,
"appState": {
"theme": template ? template.appState.theme : this.canvas.theme,
"viewBackgroundColor": template? template.appState.viewBackgroundColor : this.canvas.viewBackgroundColor
type: "excalidraw",
version: 2,
source: "https://excalidraw.com",
elements: elements,
appState: {
theme: template?.appState?.theme ?? this.canvas.theme,
viewBackgroundColor: template?.appState?.viewBackgroundColor ?? this.canvas.viewBackgroundColor,
files: template?.appState?.files ?? {}
}
},
{

View File

@@ -136,6 +136,9 @@ export class ExcalidrawData {
}
position += data.match(/((^%%\n)?# Text Elements\n)/m)[0].length
data = data.substring(position);
position = 0;
//iterating through all the text elements in .md
//Text elements always contain the raw value
const BLOCKREF_LEN:number = " ^12345678\n\n".length;

View File

@@ -32,10 +32,10 @@ import {
IMAGE_TYPES
} from './constants';
import ExcalidrawPlugin from './main';
import {estimateBounds, ExcalidrawAutomate, repositionElementsToCursor} from './ExcalidrawAutomate';
import {ExcalidrawAutomate, repositionElementsToCursor} from './ExcalidrawAutomate';
import { t } from "./lang/helpers";
import { ExcalidrawData, REG_LINKINDEX_HYPERLINK, REGEX_LINK } from "./ExcalidrawData";
import { checkAndCreateFolder, download, getNewUniqueFilepath, splitFolderAndFilename, viewportCoordsToSceneCoords } from "./Utils";
import { checkAndCreateFolder, download, getNewUniqueFilepath, splitFolderAndFilename, svgToBase64, viewportCoordsToSceneCoords } from "./Utils";
import { Prompt } from "./Prompt";
import { ClipboardData } from "@zsviczian/excalidraw/types/clipboard";
@@ -556,9 +556,9 @@ export default class ExcalidrawView extends TextFileView {
let svg = await ExcalidrawView.getSVG(this.getScene(),exportSettings);
if(!svg) return null;
svg = ExcalidrawView.embedFontsInSVG(svg);
download("data:image/svg+xml;base64",btoa(unescape(encodeURIComponent(svg.outerHTML))),this.file.basename+'.svg');
download(null,svgToBase64(svg.outerHTML),this.file.basename+'.svg');
return;
}
}
this.saveSVG()
});
})
@@ -783,10 +783,13 @@ export default class ExcalidrawView extends TextFileView {
key: "abc",
tabIndex: 0,
onKeyDown: (e:any) => {
//@ts-ignore
if(e.target === excalidrawDiv.ref.current) return; //event should originate from the canvas
if(document.fullscreenEnabled && document.fullscreenElement == this.contentEl && e.keyCode==27) {
document.exitFullscreen();
this.zoomToFit();
}
this.ctrlKeyDown = e.ctrlKey || e.metaKey;
this.shiftKeyDown = e.shiftKey;
this.altKeyDown = e.altKey;
@@ -975,7 +978,9 @@ export default class ExcalidrawView extends TextFileView {
switch(draggable?.type) {
case "file":
if (!onDropHook("file",[draggable.file],null)) {
if((event.ctrlKey || event.metaKey) && IMAGE_TYPES.contains(draggable.file.extension)) {
if((event.ctrlKey || event.metaKey)
&& (IMAGE_TYPES.contains(draggable.file.extension)
|| this.plugin.isExcalidrawFile(draggable.file))) {
const f = draggable.file;
const topX = currentPosition.x;
const topY = currentPosition.y;

View File

@@ -21,7 +21,7 @@ export class MigrationPrompt extends Modal {
createForm(): void {
const div = this.contentEl.createDiv();
div.addClass("excalidarw-prompt-div");
div.addClass("excalidraw-prompt-div");
div.style.maxWidth = "600px";
div.createEl('p',{text: "This version comes with tons of new features and possibilities. Please read the description in Community Plugins to find out more."});
div.createEl('p',{text: ""} , (el) => {

View File

@@ -3,7 +3,8 @@ import { Random } from "roughjs/bin/math";
import { Zoom } from "@zsviczian/excalidraw/types/types";
import { nanoid } from "nanoid";
import { IMAGE_TYPES } from "./constants";
import {ExcalidrawAutomate} from './ExcalidrawAutomate';
declare let window: ExcalidrawAutomate;
/**
* Splits a full path including a folderpath and a filename into separate folderpath and filename components
@@ -134,20 +135,30 @@ export const getObsidianImage = async (app: App, file: TFile)
size: {height: number, width: number},
}> => {
if(!app || !file) return null;
if (!IMAGE_TYPES.contains(file.extension)) return null;
const isExcalidrawFile = window.ExcalidrawAutomate.isExcalidrawFile(file);
if (!(IMAGE_TYPES.contains(file.extension) || isExcalidrawFile)) {
return null;
}
const ab = await app.vault.readBinary(file);
const excalidrawSVG = isExcalidrawFile
? svgToBase64((await window.ExcalidrawAutomate.createSVG(file.path,true)).outerHTML)
: null;
return {
imageId: await generateIdFromFile(ab),
dataURL: file.extension==="svg" ? await getSVGData(app,file) : await getDataURL(ab),
size: await getImageSize(app,file)
dataURL: excalidrawSVG ?? (file.extension==="svg" ? await getSVGData(app,file) : await getDataURL(ab)),
size: await getImageSize(app,excalidrawSVG??app.vault.getResourcePath(file))
}
}
const getSVGData = async (app: App, file: TFile): Promise<string> => {
const svg = await app.vault.read(file);
return "data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(svg.replaceAll("&nbsp;"," "))))
return svgToBase64(svg);
}
export const svgToBase64 = (svg:string):string => {
return "data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(svg.replaceAll("&nbsp;"," "))));
}
const getDataURL = async (file: ArrayBuffer): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
@@ -180,11 +191,11 @@ const generateIdFromFile = async (file: ArrayBuffer):Promise<string> => {
return id;
};
const getImageSize = async (app: App, file:TFile):Promise<{height:number, width:number}> => {
const getImageSize = async (app: App, src:string):Promise<{height:number, width:number}> => {
return new Promise((resolve, reject) => {
let img = new Image()
img.onload = () => resolve({height: img.height, width:img.width});
img.onerror = reject;
img.src = app.vault.getResourcePath(file);
img.src = src;
})
}

View File

@@ -55,7 +55,7 @@ import { Prompt } from "./Prompt";
import { around } from "monkey-around";
import { t } from "./lang/helpers";
import { MigrationPrompt } from "./MigrationPrompt";
import { checkAndCreateFolder, download, getIMGPathFromExcalidrawFile, getNewUniqueFilepath, splitFolderAndFilename } from "./Utils";
import { checkAndCreateFolder, download, getIMGPathFromExcalidrawFile, getNewUniqueFilepath, splitFolderAndFilename, svgToBase64 } from "./Utils";
declare module "obsidian" {
interface App {
@@ -239,7 +239,7 @@ export default class ExcalidrawPlugin extends Plugin {
svg = ExcalidrawView.embedFontsInSVG(svg);
svg.removeAttribute('width');
svg.removeAttribute('height');
img.setAttribute("src","data:image/svg+xml;base64,"+btoa(unescape(encodeURIComponent(svg.outerHTML.replaceAll("&nbsp;"," ")))));
img.setAttribute("src",svgToBase64(svg.outerHTML));
return img;
}
@@ -1179,3 +1179,4 @@ export default class ExcalidrawPlugin extends Plugin {
}
}

1290
yarn.lock

File diff suppressed because it is too large Load Diff