Merge branch 'Image-Element' into temp-master

This commit is contained in:
zsviczian
2021-10-11 19:59:10 +02:00
committed by GitHub
8 changed files with 1328 additions and 1175 deletions

View File

@@ -1,40 +1,40 @@
{
"name": "obsidian-excalidraw-plugin",
"version": "1.1.10",
"description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings",
"main": "main.js",
"scripts": {
"dev": "cross-env NODE_ENV=development rollup --config rollup.config.js -w",
"build": "cross-env NODE_ENV=production rollup --config rollup.config.js"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@zsviczian/excalidraw": "0.9.0-obsidian-12",
"monkey-around": "^2.2.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "^1.1.5",
"roughjs": "4.4.1"
},
"devDependencies": {
"@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": "^21.0.0",
"@rollup/plugin-node-resolve": "^13.0.5",
"@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.5",
"@types/node": "^15.12.4",
"@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.2",
"tslib": "^2.3.1",
"typescript": "^4.4.3"
}
}
{
"name": "obsidian-excalidraw-plugin",
"version": "1.3.15",
"description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings",
"main": "main.js",
"scripts": {
"dev": "cross-env NODE_ENV=development rollup --config rollup.config.js -w",
"build": "cross-env NODE_ENV=production rollup --config rollup.config.js"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@zsviczian/excalidraw": "0.9.0-obsidian-image-support-5",
"monkey-around": "^2.2.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "^1.1.5",
"roughjs": "4.4.1"
},
"devDependencies": {
"@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": "^21.0.0",
"@rollup/plugin-node-resolve": "^13.0.5",
"@rollup/plugin-replace": "^2.4.2",
"@rollup/plugin-typescript": "^8.2.5",
"@types/node": "^15.12.4",
"@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.2",
"tslib": "^2.3.1",
"typescript": "^4.4.3"
}
}

View File

@@ -15,9 +15,10 @@ import {
FRONTMATTER,
nanoid,
JSON_parse,
VIEW_TYPE_EXCALIDRAW
VIEW_TYPE_EXCALIDRAW,
MAX_IMAGE_SIZE
} from "./constants";
import { wrapText } from "./Utils";
import { getObsidianImage, wrapText } from "./Utils";
import { AppState } from "@zsviczian/excalidraw/types/types";
declare type ConnectionPoint = "top"|"bottom"|"left"|"right";
@@ -26,6 +27,7 @@ export interface ExcalidrawAutomate extends Window {
ExcalidrawAutomate: {
plugin: ExcalidrawPlugin;
elementsDict: {};
imagesDict: {};
style: {
strokeColor: string;
backgroundColor: string;
@@ -71,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;
@@ -102,6 +104,7 @@ export interface ExcalidrawAutomate extends Window {
endObjectId?:string
}
):string ;
addImage(topX:number, topY:number, imageFile: TFile):Promise<string>;
connectObjects (
objectA: string,
connectionA: ConnectionPoint,
@@ -160,6 +163,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
window.ExcalidrawAutomate = {
plugin: plugin,
elementsDict: {},
imagesDict: {},
style: {
strokeColor: "#000000",
backgroundColor: "transparent",
@@ -308,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;
@@ -356,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 ?? {}
}
},
{
@@ -513,6 +521,25 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
}
return id;
},
async addImage(topX:number, topY:number, imageFile: TFile):Promise<string> {
const id = nanoid();
const image = await getObsidianImage(this.plugin.app,imageFile);
if(!image) return null;
this.imagesDict[image.imageId] = {
type:"image",
id: image.imageId,
dataURL: image.dataURL
}
if (Math.max(image.size.width,image.size.height) > MAX_IMAGE_SIZE) {
const scale = MAX_IMAGE_SIZE/Math.max(image.size.width,image.size.height);
image.size.width = scale*image.size.width;
image.size.height = scale*image.size.height;
}
this.elementsDict[id] = boxedElement(id,"image",topX,topY,image.size.width,image.size.height);
this.elementsDict[id].imageId = image.imageId;
this.elementsDict[id].scale = [1,1];
return id;
},
connectObjects(objectA: string, connectionA: ConnectionPoint, objectB: string, connectionB: ConnectionPoint, formatting?:{numberOfPoints?: number,startArrowHead?:string,endArrowHead?:string, padding?: number}):void {
if(!(this.elementsDict[objectA] && this.elementsDict[objectB])) {
return;
@@ -552,6 +579,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
},
clear() {
this.elementsDict = {};
this.imagesDict = {};
},
reset() {
this.clear();
@@ -682,7 +710,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin) {
return false;
}
const elements = this.getElements();
return await this.targetView.addElements(elements,repositionToCursor,save);
return await this.targetView.addElements(elements,repositionToCursor,save,this.imagesDict);
},
onDropHook:null,
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,9 @@
import { normalizePath, TAbstractFile, TFolder, Vault, WorkspaceLeaf } from "obsidian";
import { App, normalizePath, TAbstractFile, TFile, TFolder, Vault, WorkspaceLeaf } from "obsidian";
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';
import ExcalidrawPlugin from "./main";
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/element/types";
@@ -9,6 +12,7 @@ declare module "obsidian" {
getAdjacentLeafInDirection(leaf: WorkspaceLeaf, direction: string): WorkspaceLeaf;
}
}
declare let window: ExcalidrawAutomate;
/**
* Splits a full path including a folderpath and a filename into separate folderpath and filename components
@@ -173,4 +177,76 @@ export const getNewOrAdjacentLeaf = (plugin: ExcalidrawPlugin, leaf: WorkspaceLe
return leafToUse;
}
return plugin.app.workspace.createLeafBySplit(leaf);
}
export const getObsidianImage = async (app: App, file: TFile)
:Promise<{
imageId: string,
dataURL: string,
size: {height: number, width: number},
}> => {
if(!app || !file) 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: 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 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();
reader.onload = () => {
const dataURL = reader.result as string;
resolve(dataURL);
};
reader.onerror = (error) => reject(error);
reader.readAsDataURL(new Blob([new Uint8Array(file)]));
});
};
const generateIdFromFile = async (file: ArrayBuffer):Promise<string> => {
let id: string;
try {
const hashBuffer = await window.crypto.subtle.digest(
"SHA-1",
file,
);
id =
// convert buffer to byte array
Array.from(new Uint8Array(hashBuffer))
// convert to hex string
.map((byte) => byte.toString(16).padStart(2, "0"))
.join("");
} catch (error) {
console.error(error);
id = nanoid(40);
}
return id;
};
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 = src;
})
}

View File

@@ -3,6 +3,8 @@ export function JSON_parse(x:string):any {return JSON.parse(x.replaceAll("&#91;"
import {customAlphabet} from "nanoid";
export const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',8);
export const IMAGE_TYPES = ['jpeg', 'jpg', 'png', 'gif', 'svg', 'bmp'];
export const MAX_IMAGE_SIZE = 600;
export const FRONTMATTER_KEY = "excalidraw-plugin";
export const FRONTMATTER_KEY_CUSTOM_PREFIX = "excalidraw-link-prefix";
export const FRONTMATTER_KEY_CUSTOM_URL_PREFIX = "excalidraw-url-prefix";

View File

@@ -44,6 +44,8 @@ export default {
NOFILE: "Excalidraw (no file)",
COMPATIBILITY_MODE: "*.excalidraw file opened in compatibility mode. Convert to new format for full plugin functionality.",
CONVERT_FILE: "Convert to new format",
DRAWING_CONTAINS_IMAGE: "Warning! The drawing contains image elements. Depending on the number and size of the images, " +
"loading Markdown View may take a while. Please be patient. ",
//settings.ts
FOLDER_NAME: "Excalidraw folder",
@@ -102,7 +104,7 @@ export default {
TRANSCLUSION_WRAP_NAME: "Overflow wrap behavior of transcluded text",
TRANSCLUSION_WRAP_DESC: "Number specifies the character count where the text should be wrapped. " +
"Set the text wrapping behavior of transcluded text. Turn this ON to force-wrap " +
"text (i.e. no overflow), or OFF to soft-warp text (at the nearest whitespace).",
"text (i.e. no overflow), or OFF to soft-wrap text (at the nearest whitespace).",
PAGE_TRANSCLUSION_CHARCOUNT_NAME: "Page transclusion max char count",
PAGE_TRANSCLUSION_CHARCOUNT_DESC: "The maximum number of characters to display from the page when transcluding an entire page with the "+
"![[markdown page]] format.",

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;
}
@@ -1110,7 +1110,7 @@ export default class ExcalidrawPlugin extends Plugin {
return '%%\n# Drawing\n'
+ String.fromCharCode(96)+String.fromCharCode(96)+String.fromCharCode(96)+'json\n'
+ jsonString + '\n'
+ String.fromCharCode(96)+String.fromCharCode(96)+String.fromCharCode(96) + '\n%%';
+ String.fromCharCode(96)+String.fromCharCode(96)+String.fromCharCode(96) + '%%';
}
/**
@@ -1179,3 +1179,4 @@ export default class ExcalidrawPlugin extends Plugin {
}
}

View File

@@ -1062,10 +1062,10 @@
dependencies:
"@types/estree" "*"
"@zsviczian/excalidraw@0.9.0-obsidian-12":
"integrity" "sha512-b3TgSXfvCAY+1pbvUfPoybijvaye35rqlGc5ZrNUdvEfoNwfTRuIa7O9eWqHGFH67Q959MYa8kXWwzghwWw5PA=="
"resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.9.0-obsidian-12.tgz"
"version" "0.9.0-obsidian-12"
"@zsviczian/excalidraw@0.9.0-obsidian-image-support-5":
"integrity" "sha512-Tw3qmG8iaPQl4KVlRtnOwN6Z0bcB1XUIfGmcjSo95CQ2peo4ccKdFvOZOeYarKW6N21PX40e4bapP/+ukjsnPA=="
"resolved" "https://registry.npmjs.org/@zsviczian/excalidraw/-/excalidraw-0.9.0-obsidian-image-support-5.tgz"
"version" "0.9.0-obsidian-image-support-5"
"abab@^1.0.3":
"integrity" "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4="