mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
1.9.20
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "1.9.19",
|
||||
"version": "1.9.20",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@zsviczian/excalidraw": "0.15.3-obsidian-1",
|
||||
"@zsviczian/excalidraw": "0.16.1-obsidian-2",
|
||||
"chroma-js": "^2.4.2",
|
||||
"clsx": "^2.0.0",
|
||||
"colormaster": "^1.2.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//https://stackoverflow.com/questions/2068344/how-do-i-get-a-youtube-video-thumbnail-from-the-youtube-api
|
||||
//https://img.youtube.com/vi/uZz5MgzWXiM/maxresdefault.jpg
|
||||
|
||||
import { ExcalidrawImageElement, FileId } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { ExcalidrawElement, ExcalidrawImageElement, FileId } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { BinaryFileData, DataURL } from "@zsviczian/excalidraw/types/types";
|
||||
import { App, MarkdownRenderer, Notice, TFile } from "obsidian";
|
||||
import {
|
||||
@@ -40,6 +40,8 @@ import {
|
||||
} from "./utils/Utils";
|
||||
import { ValueOf } from "./types";
|
||||
import { has } from "./svgToExcalidraw/attributes";
|
||||
import { getMermaidImageElements, getMermaidText, shouldRenderMermaid } from "./utils/MermaidUtils";
|
||||
import { mermaidToExcalidraw } from "src/constants";
|
||||
|
||||
//An ugly workaround for the following situation.
|
||||
//File A is a markdown file that has an embedded Excalidraw file B
|
||||
@@ -317,6 +319,70 @@ export class EmbeddedFilesLoader {
|
||||
return result;
|
||||
}
|
||||
|
||||
private async getExcalidrawSVG ({
|
||||
isDark,
|
||||
file,
|
||||
depth,
|
||||
inFile,
|
||||
hasSVGwithBitmap,
|
||||
elements = [],
|
||||
}: {
|
||||
isDark: boolean;
|
||||
file: TFile;
|
||||
depth: number;
|
||||
inFile: TFile | EmbeddedFile;
|
||||
hasSVGwithBitmap: boolean;
|
||||
elements?: ExcalidrawElement[];
|
||||
}) : Promise<{dataURL: DataURL, hasSVGwithBitmap:boolean}> {
|
||||
//debug({where:"EmbeddedFileLoader.getExcalidrawSVG",uid:this.uid,file:file.name});
|
||||
const forceTheme = hasExportTheme(this.plugin, file)
|
||||
? getExportTheme(this.plugin, file, "light")
|
||||
: undefined;
|
||||
const exportSettings: ExportSettings = {
|
||||
withBackground: hasExportBackground(this.plugin, file)
|
||||
? getWithBackground(this.plugin, file)
|
||||
: false,
|
||||
withTheme: !!forceTheme,
|
||||
};
|
||||
const svg = replaceSVGColors(
|
||||
await createSVG(
|
||||
file?.path,
|
||||
true,
|
||||
exportSettings,
|
||||
this,
|
||||
forceTheme,
|
||||
null,
|
||||
null,
|
||||
elements,
|
||||
this.plugin,
|
||||
depth+1,
|
||||
getExportPadding(this.plugin, file),
|
||||
),
|
||||
inFile instanceof EmbeddedFile ? inFile.colorMap : null
|
||||
) as SVGSVGElement;
|
||||
|
||||
//https://stackoverflow.com/questions/51154171/remove-css-filter-on-child-elements
|
||||
const imageList = svg.querySelectorAll(
|
||||
"image:not([href^='data:image/svg'])",
|
||||
);
|
||||
if (imageList.length > 0) {
|
||||
hasSVGwithBitmap = true;
|
||||
}
|
||||
if (hasSVGwithBitmap && isDark) {
|
||||
imageList.forEach((i) => {
|
||||
const id = i.parentElement?.id;
|
||||
svg.querySelectorAll(`use[href='#${id}']`).forEach((u) => {
|
||||
u.setAttribute("filter", THEME_FILTER);
|
||||
});
|
||||
});
|
||||
}
|
||||
if (!hasSVGwithBitmap && svg.getAttribute("hasbitmap")) {
|
||||
hasSVGwithBitmap = true;
|
||||
}
|
||||
const dURL = svgToBase64(svg.outerHTML) as DataURL;
|
||||
return {dataURL: dURL as DataURL, hasSVGwithBitmap};
|
||||
};
|
||||
|
||||
private async _getObsidianImage(inFile: TFile | EmbeddedFile, depth: number): Promise<ImgData> {
|
||||
if (!this.plugin || !inFile) {
|
||||
return null;
|
||||
@@ -363,59 +429,20 @@ export class EmbeddedFilesLoader {
|
||||
? null
|
||||
: await app.vault.readBinary(file);
|
||||
|
||||
const getExcalidrawSVG = async (isDark: boolean) => {
|
||||
//debug({where:"EmbeddedFileLoader.getExcalidrawSVG",uid:this.uid,file:file.name});
|
||||
const forceTheme = hasExportTheme(this.plugin, file)
|
||||
? getExportTheme(this.plugin, file, "light")
|
||||
: undefined;
|
||||
const exportSettings: ExportSettings = {
|
||||
withBackground: hasExportBackground(this.plugin, file)
|
||||
? getWithBackground(this.plugin, file)
|
||||
: false,
|
||||
withTheme: !!forceTheme,
|
||||
};
|
||||
const svg = replaceSVGColors(
|
||||
await createSVG(
|
||||
file.path,
|
||||
true,
|
||||
exportSettings,
|
||||
this,
|
||||
forceTheme,
|
||||
null,
|
||||
null,
|
||||
[],
|
||||
this.plugin,
|
||||
depth+1,
|
||||
getExportPadding(this.plugin, file),
|
||||
),
|
||||
inFile instanceof EmbeddedFile ? inFile.colorMap : null
|
||||
) as SVGSVGElement;
|
||||
let dURL: DataURL = null;
|
||||
if (isExcalidrawFile) {
|
||||
const res = await this.getExcalidrawSVG({
|
||||
isDark: this.isDark,
|
||||
file,
|
||||
depth,
|
||||
inFile,
|
||||
hasSVGwithBitmap,
|
||||
});
|
||||
dURL = res.dataURL;
|
||||
hasSVGwithBitmap = res.hasSVGwithBitmap;
|
||||
}
|
||||
|
||||
//https://stackoverflow.com/questions/51154171/remove-css-filter-on-child-elements
|
||||
const imageList = svg.querySelectorAll(
|
||||
"image:not([href^='data:image/svg'])",
|
||||
);
|
||||
if (imageList.length > 0) {
|
||||
hasSVGwithBitmap = true;
|
||||
}
|
||||
if (hasSVGwithBitmap && isDark) {
|
||||
imageList.forEach((i) => {
|
||||
const id = i.parentElement?.id;
|
||||
svg.querySelectorAll(`use[href='#${id}']`).forEach((u) => {
|
||||
u.setAttribute("filter", THEME_FILTER);
|
||||
});
|
||||
});
|
||||
}
|
||||
if (!hasSVGwithBitmap && svg.getAttribute("hasbitmap")) {
|
||||
hasSVGwithBitmap = true;
|
||||
}
|
||||
const dURL = svgToBase64(svg.outerHTML) as DataURL;
|
||||
return dURL as DataURL;
|
||||
};
|
||||
|
||||
const excalidrawSVG = isExcalidrawFile
|
||||
? await getExcalidrawSVG(this.isDark)
|
||||
: null;
|
||||
const excalidrawSVG = isExcalidrawFile ? dURL : null;
|
||||
|
||||
const [pdfDataURL, pdfSize] = isPDF
|
||||
? await this.pdfToDataURL(file,linkParts)
|
||||
@@ -550,6 +577,59 @@ export class EmbeddedFilesLoader {
|
||||
}
|
||||
}
|
||||
|
||||
if(shouldRenderMermaid()) {
|
||||
const mermaidElements = getMermaidImageElements(excalidrawData.scene.elements);
|
||||
for(const element of mermaidElements) {
|
||||
if(this.terminate) {
|
||||
continue;
|
||||
}
|
||||
const data = getMermaidText(element);
|
||||
const result = await mermaidToExcalidraw(data, {fontSize: 20});
|
||||
if(!result) {
|
||||
continue;
|
||||
}
|
||||
if(result?.files) {
|
||||
for (const key in result.files) {
|
||||
const fileData = {
|
||||
...result.files[key],
|
||||
id: element.fileId,
|
||||
created: Date.now(),
|
||||
hasSVGwithBitmap: false,
|
||||
shouldScale: true,
|
||||
size: await getImageSize(result.files[key].dataURL),
|
||||
};
|
||||
files.push(fileData);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if(result?.elements) {
|
||||
//handle case that mermaidToExcalidraw has implemented this type of diagram in the mean time
|
||||
const res = await this.getExcalidrawSVG({
|
||||
isDark: this.isDark,
|
||||
file: null,
|
||||
depth,
|
||||
inFile: null,
|
||||
hasSVGwithBitmap: false,
|
||||
elements: result.elements
|
||||
});
|
||||
if(res?.dataURL) {
|
||||
const size = await getImageSize(res.dataURL);
|
||||
const fileData:FileData = {
|
||||
mimeType: "image/svg+xml",
|
||||
id: element.fileId,
|
||||
dataURL: res.dataURL,
|
||||
created: Date.now(),
|
||||
hasSVGwithBitmap: res.hasSVGwithBitmap,
|
||||
size,
|
||||
shouldScale: true,
|
||||
};
|
||||
files.push(fileData);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.emptyPDFDocsMap();
|
||||
if (this.terminate) {
|
||||
return;
|
||||
|
||||
@@ -2312,7 +2312,7 @@ export class ExcalidrawAutomate {
|
||||
* Gets the class PolyBool from https://github.com/velipso/polybooljs
|
||||
* @returns
|
||||
*/
|
||||
getPolyool() {
|
||||
getPolyBool() {
|
||||
const defaultEpsilon = 0.0000000001;
|
||||
PolyBool.epsilon(defaultEpsilon);
|
||||
return PolyBool;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
originalText: this is the text without added linebreaks for wrapping. This will be parsed or markup depending on view mode
|
||||
rawText: text with original markdown markup and without the added linebreaks for wrapping
|
||||
*/
|
||||
import { App, TFile } from "obsidian";
|
||||
import { App, Notice, TFile } from "obsidian";
|
||||
import {
|
||||
nanoid,
|
||||
FRONTMATTER_KEY_CUSTOM_PREFIX,
|
||||
@@ -52,7 +52,8 @@ import {
|
||||
} from "@zsviczian/excalidraw/types/element/types";
|
||||
import { BinaryFiles, DataURL, SceneData } from "@zsviczian/excalidraw/types/types";
|
||||
import { EmbeddedFile, MimeType } from "./EmbeddedFileLoader";
|
||||
import { ConfirmationPrompt, Prompt } from "./dialogs/Prompt";
|
||||
import { ConfirmationPrompt } from "./dialogs/Prompt";
|
||||
import { getMermaidImageElements, getMermaidText, shouldRenderMermaid } from "./utils/MermaidUtils";
|
||||
|
||||
type SceneDataWithFiles = SceneData & { files: BinaryFiles };
|
||||
|
||||
@@ -261,6 +262,7 @@ export class ExcalidrawData {
|
||||
public loaded: boolean = false;
|
||||
public files: Map<FileId, EmbeddedFile> = null; //fileId, path
|
||||
private equations: Map<FileId, { latex: string; isLoaded: boolean }> = null; //fileId, path
|
||||
private mermaids: Map<FileId, { mermaid: string; isLoaded: boolean }> = null; //fileId, path
|
||||
private compatibilityMode: boolean = false;
|
||||
selectedElementIds: {[key:string]:boolean} = {}; //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/609
|
||||
|
||||
@@ -270,6 +272,7 @@ export class ExcalidrawData {
|
||||
this.app = plugin.app;
|
||||
this.files = new Map<FileId, EmbeddedFile>();
|
||||
this.equations = new Map<FileId, { latex: string; isLoaded: boolean }>();
|
||||
this.mermaids = new Map<FileId, { mermaid: string; isLoaded: boolean }>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -434,9 +437,10 @@ export class ExcalidrawData {
|
||||
>();
|
||||
this.elementLinks = new Map<string, string>();
|
||||
if (this.file != file) {
|
||||
//this is a reload - files and equations will take care of reloading when needed
|
||||
//this is a reload - files, equations and mermaids will take care of reloading when needed
|
||||
this.files.clear();
|
||||
this.equations.clear();
|
||||
this.mermaids.clear();
|
||||
}
|
||||
this.file = file;
|
||||
this.compatibilityMode = false;
|
||||
@@ -621,6 +625,16 @@ export class ExcalidrawData {
|
||||
});
|
||||
}
|
||||
|
||||
//Load Mermaids
|
||||
const mermaidElements = getMermaidImageElements(this.scene.elements);
|
||||
if(mermaidElements.length>0 && !shouldRenderMermaid()) {
|
||||
new Notice ("Mermaid images are only supported in Obsidian 1.4.14 and above. Please update Obsidian to see the mermaid images in this drawing. Obsidian mobile 1.4.14 currently only avaiable to Obsidian insiders", 5000);
|
||||
} else {
|
||||
mermaidElements.forEach(el =>
|
||||
this.setMermaid(el.fileId, {mermaid: getMermaidText(el), isLoaded: false})
|
||||
);
|
||||
}
|
||||
|
||||
//Check to see if there are text elements in the JSON that were missed from the # Text Elements section
|
||||
//e.g. if the entire text elements section was deleted.
|
||||
this.findNewTextElementsInScene();
|
||||
@@ -657,6 +671,7 @@ export class ExcalidrawData {
|
||||
}
|
||||
this.files.clear();
|
||||
this.equations.clear();
|
||||
this.mermaids.clear();
|
||||
this.findNewTextElementsInScene();
|
||||
this.findNewElementLinksInScene();
|
||||
await this.setTextMode(TextMode.raw, true); //legacy files are always displayed in raw mode.
|
||||
@@ -1103,6 +1118,7 @@ export class ExcalidrawData {
|
||||
outString += `${this.elementLinks.get(key)} ^${key}\n\n`;
|
||||
}
|
||||
|
||||
// deliberately not adding mermaids to here. It is enough to have the mermaidText in the image element's customData
|
||||
outString +=
|
||||
this.equations.size > 0 || this.files.size > 0
|
||||
? "\n# Embedded files\n"
|
||||
@@ -1226,6 +1242,13 @@ export class ExcalidrawData {
|
||||
}
|
||||
});
|
||||
|
||||
this.mermaids.forEach((value, key) => {
|
||||
if (!fileIds.contains(key)) {
|
||||
this.mermaids.delete(key);
|
||||
dirty = true;
|
||||
}
|
||||
});
|
||||
|
||||
//check if there are any images that need to be processed in the new scene
|
||||
if (!scene.files || Object.keys(scene.files).length === 0) {
|
||||
return false;
|
||||
@@ -1239,25 +1262,25 @@ export class ExcalidrawData {
|
||||
fileIds.forEach(fileId=>{
|
||||
if(processedIds.has(fileId)) {
|
||||
const file = this.getFile(fileId);
|
||||
//const file = this.files.get(fileId as FileId);
|
||||
const equation = this.getEquation(fileId);
|
||||
//const equation = this.equations.get(fileId as FileId);
|
||||
//images should have a single reference, but equations and markdown embeds should have as many as instances of the file in the scene
|
||||
const mermaid = this.getMermaid(fileId);
|
||||
|
||||
//images should have a single reference, but equations, and markdown embeds should have as many as instances of the file in the scene
|
||||
if(file && (file.isHyperlink || (file.file && (file.file.extension !== "md" || this.plugin.isExcalidrawFile(file.file))))) {
|
||||
return;
|
||||
}
|
||||
if(mermaid) {
|
||||
return;
|
||||
}
|
||||
const newId = fileid();
|
||||
//scene.files[newId] = {...scene.files[fileId]};
|
||||
(scene.elements.filter((el:ExcalidrawImageElement)=>el.fileId === fileId)[0] as any).fileId = newId;
|
||||
dirty = true;
|
||||
processedIds.add(newId);
|
||||
if(file) {
|
||||
this.setFile(newId as FileId,new EmbeddedFile(this.plugin,this.file.path,file.linkParts.original));
|
||||
//this.files.set(newId as FileId,new EmbeddedFile(this.plugin,this.file.path,file.linkParts.original))
|
||||
}
|
||||
if(equation) {
|
||||
this.setEquation(newId as FileId, {latex:equation.latex, isLoaded:false});
|
||||
//this.equations.set(newId as FileId, equation);
|
||||
}
|
||||
}
|
||||
processedIds.add(fileId);
|
||||
@@ -1265,7 +1288,8 @@ export class ExcalidrawData {
|
||||
|
||||
|
||||
for (const key of Object.keys(scene.files)) {
|
||||
if (!(this.hasFile(key as FileId) || this.hasEquation(key as FileId))) {
|
||||
const mermaidElements = getMermaidImageElements(scene.elements.filter((el:ExcalidrawImageElement)=>el.fileId === key));
|
||||
if (!(this.hasFile(key as FileId) || this.hasEquation(key as FileId) || this.hasMermaid(key as FileId) || mermaidElements.length > 0)) {
|
||||
dirty = true;
|
||||
await this.saveDataURLtoVault(
|
||||
scene.files[key].dataURL,
|
||||
@@ -1275,35 +1299,6 @@ export class ExcalidrawData {
|
||||
}
|
||||
}
|
||||
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/297
|
||||
/*const equations = new Set<string>();
|
||||
const duplicateEqs = new Set<string>();
|
||||
for (const key of fileIds) {
|
||||
if (this.hasEquation(key as FileId)) {
|
||||
if (equations.has(key)) {
|
||||
duplicateEqs.add(key);
|
||||
} else {
|
||||
equations.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (duplicateEqs.size > 0) {
|
||||
for (const key of duplicateEqs.keys()) {
|
||||
const elements = this.scene.elements.filter(
|
||||
(el: ExcalidrawElement) => el.type === "image" && el.fileId === key,
|
||||
);
|
||||
for (let i = 1; i < elements.length; i++) {
|
||||
const newFileId = fileid() as FileId;
|
||||
this.setEquation(newFileId, {
|
||||
latex: this.getEquation(key as FileId).latex,
|
||||
isLoaded: false,
|
||||
});
|
||||
elements[i].fileId = newFileId;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
@@ -1558,7 +1553,7 @@ export class ExcalidrawData {
|
||||
}
|
||||
|
||||
/**
|
||||
Files and equations copy/paste support
|
||||
Files, equations and mermaid copy/paste support
|
||||
This is not a complete solution, it assumes the source document is opened first
|
||||
at that time the fileId is stored in the master files/equations map
|
||||
when pasted the map is checked if the file already exists
|
||||
@@ -1663,6 +1658,9 @@ export class ExcalidrawData {
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------
|
||||
//Equations
|
||||
//--------------
|
||||
public setEquation(
|
||||
fileId: FileId,
|
||||
data: { latex: string; isLoaded: boolean },
|
||||
@@ -1704,6 +1702,51 @@ export class ExcalidrawData {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------
|
||||
//Mermaids
|
||||
//--------------
|
||||
public setMermaid(
|
||||
fileId: FileId,
|
||||
data: { mermaid: string; isLoaded: boolean },
|
||||
) {
|
||||
this.mermaids.set(fileId, { mermaid: data.mermaid, isLoaded: data.isLoaded });
|
||||
this.plugin.mermaidsMaster.set(fileId, data.mermaid);
|
||||
}
|
||||
|
||||
public getMermaid(fileId: FileId): { mermaid: string; isLoaded: boolean } {
|
||||
let result = this.mermaids.get(fileId);
|
||||
if(result) return result;
|
||||
const mermaid = this.plugin.mermaidsMaster.get(fileId);
|
||||
if(!mermaid) return result;
|
||||
this.mermaids.set(fileId, {mermaid, isLoaded: false});
|
||||
return {mermaid, isLoaded: false};
|
||||
}
|
||||
|
||||
public getMermaidEntries() {
|
||||
return this.mermaids.entries();
|
||||
}
|
||||
|
||||
public deleteMermaid(fileId: FileId) {
|
||||
this.mermaids.delete(fileId);
|
||||
//deliberately not deleting from plugin.mermaidsMaster
|
||||
//could be present in other drawings as well
|
||||
}
|
||||
|
||||
//Image copy/paste support
|
||||
public hasMermaid(fileId: FileId): boolean {
|
||||
if (this.mermaids.has(fileId)) {
|
||||
return true;
|
||||
}
|
||||
if (this.plugin.mermaidsMaster.has(fileId)) {
|
||||
this.mermaids.set(fileId, {
|
||||
mermaid: this.plugin.mermaidsMaster.get(fileId),
|
||||
isLoaded: false,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const getTransclusion = async (
|
||||
|
||||
@@ -177,7 +177,7 @@ const _getSVGIMG = async ({filenameParts,theme,cacheReady,img,file,exportSetting
|
||||
return null;
|
||||
}
|
||||
|
||||
svg = embedFontsInSVG(svg, plugin);
|
||||
svg = embedFontsInSVG(svg, plugin, false);
|
||||
//need to remove width and height attributes to support area= embeds
|
||||
svg.removeAttribute("width");
|
||||
svg.removeAttribute("height");
|
||||
@@ -199,7 +199,7 @@ const _getSVGNative = async ({filenameParts,theme,cacheReady,containerElement,fi
|
||||
maybeSVG = await imageCache.getImageFromCache(cacheKey);
|
||||
}
|
||||
|
||||
const svg = maybeSVG && (maybeSVG instanceof SVGSVGElement)
|
||||
let svg = maybeSVG && (maybeSVG instanceof SVGSVGElement)
|
||||
? maybeSVG
|
||||
: convertSVGStringToElement((await createSVG(
|
||||
filenameParts.hasGroupref || filenameParts.hasBlockref || filenameParts.hasSectionref || filenameParts.hasFrameref
|
||||
@@ -223,6 +223,7 @@ const _getSVGNative = async ({filenameParts,theme,cacheReady,containerElement,fi
|
||||
return null;
|
||||
}
|
||||
|
||||
svg = embedFontsInSVG(svg, plugin, true);
|
||||
svg.removeAttribute("width");
|
||||
svg.removeAttribute("height");
|
||||
containerElement.append(svg);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -17,6 +17,49 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
|
||||
|
||||
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://cdn.ko-fi.com/cdn/kofi3.png?v=3" height=45></a></div>
|
||||
`,
|
||||
"1.9.20":`
|
||||
<div class="excalidraw-videoWrapper"><div>
|
||||
<iframe src="https://www.youtube.com/embed/QB2rKRxxYlg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
</div></div>
|
||||
|
||||
## Fixed
|
||||
- Fourth Font displays correctly in SVG embeds mode
|
||||
- The re-colorMap map (see [1.9.19](https://github.com/zsviczian/obsidian-excalidraw-plugin/releases/tag/1.9.19) for more info) did not work when either of the fill or stroke color properties of the image was missing.
|
||||
- Excalidraw Pasting with middle mouse button on Linux [#1338](https://github.com/zsviczian/obsidian-excalidraw-plugin/pull/1338) 🙏@Aeases
|
||||
|
||||
### Fixed by excalidraw.com
|
||||
- Excalidraw's native eyedropper fixes [#7019](https://github.com/excalidraw/excalidraw/pull/7019)
|
||||
|
||||
## New
|
||||
- Now you can insert [Mermaid](https://mermaid.live/) diagrams as Excalidraw elements into your drawings (currently only the [Flowchart](https://mermaid.js.org/syntax/flowchart.html) type is supported, [other diagram types](https://mermaid.js.org/intro/#diagram-types) are inserted as Mermaid native images.
|
||||
- ⚠️**This feature requires Obsidian API v1.4.14 (the latest desktop version). On Obsidian mobile API v1.4.14 is only available to Obsidian insiders currently**
|
||||
- If you want to contribute to the project please head over to [mermaid-to-excalidraw](https://github.com/excalidraw/mermaid-to-excalidraw) and help create the converters for the other diagram types.
|
||||
- The Fourth Font now also supports the OTF format
|
||||
- Disable snap-to-grid in grid mode by holding down the CTRL/CMD while drawing or moving an element [#6983](https://github.com/excalidraw/excalidraw/pull/6983)
|
||||
- I updated the Excalidraw logo in Obsidian. This affects the logo on the tab and the ribbon.
|
||||
|
||||
### New from excalidraw.com
|
||||
- Elements alignment snapping. Hold down the CTRL/CMD button while moving an element to snap it to other objects. [#6256](https://github.com/excalidraw/excalidraw/pull/6256)
|
||||
|
||||
### New in the script library
|
||||
- The amazing shape [Boolean Operations](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Boolean%20Operations.md) script created by 🙏@GColoy is available in the script library.
|
||||
|
||||
### New in Excalidraw Automate
|
||||
- ${String.fromCharCode(96)}getPolyBool()${String.fromCharCode(96)} returns a [PolyBool](https://github.com/velipso/polybooljs) object
|
||||
- sample mermaid code:
|
||||
${String.fromCharCode(96,96,96)}js
|
||||
ea = ExcalidrawAutomate();
|
||||
ea.setView();
|
||||
await ea.addMermaid(
|
||||
${String.fromCharCode(96)}flowchart TD
|
||||
A[Christmas] -->|Get money| B(Go shopping)
|
||||
B --> C{Let me think}
|
||||
C -->|One| D[Laptop]
|
||||
C -->|Two| E[iPhone]
|
||||
C -->|Three| F[fa:fa-car Car]${String.fromCharCode(96)}
|
||||
);
|
||||
ea.addElementsToView();
|
||||
${String.fromCharCode(96,96,96)}`,
|
||||
"1.9.19":`
|
||||
## New
|
||||
- I added new features to the [Deconstruct Selected Elements](https://github.com/zsviczian/obsidian-excalidraw-plugin/blob/master/ea-scripts/Deconstruct%20selected%20elements%20into%20new%20drawing.md) script
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
Workspace,
|
||||
Editor,
|
||||
MarkdownFileInfo,
|
||||
loadMermaid,
|
||||
} from "obsidian";
|
||||
import {
|
||||
BLANK_DRAWING,
|
||||
@@ -146,6 +147,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
public filesMaster: Map<FileId, { isHyperlink: boolean; path: string; hasSVGwithBitmap: boolean; blockrefData: string, colorMapJSON?: string}> =
|
||||
null; //fileId, path
|
||||
public equationsMaster: Map<FileId, string> = null; //fileId, formula
|
||||
public mermaidsMaster: Map<FileId, string> = null; //fileId, mermaidText
|
||||
public mathjax: any = null;
|
||||
private mathjaxDiv: HTMLDivElement = null;
|
||||
public mathjaxLoaderFinished: boolean = false;
|
||||
@@ -164,6 +166,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
{ isHyperlink: boolean; path: string; hasSVGwithBitmap: boolean; blockrefData: string; colorMapJSON?: string }
|
||||
>();
|
||||
this.equationsMaster = new Map<FileId, string>();
|
||||
this.mermaidsMaster = new Map<FileId, string>();
|
||||
}
|
||||
|
||||
public getPackage(win:Window):Packages {
|
||||
@@ -190,6 +193,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
addIcon(EXPORT_IMG_ICON_NAME, EXPORT_IMG_ICON);
|
||||
|
||||
await this.loadSettings({reEnableAutosave:true});
|
||||
await loadMermaid();
|
||||
imageCache.plugin = this;
|
||||
|
||||
this.addSettingTab(new ExcalidrawSettingTab(this.app, this));
|
||||
|
||||
14
src/utils/MermaidUtils.ts
Normal file
14
src/utils/MermaidUtils.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ExcalidrawElement, ExcalidrawImageElement } from "@zsviczian/excalidraw/types/element/types";
|
||||
import { requireApiVersion } from "obsidian";
|
||||
|
||||
export const getMermaidImageElements = (elements: ExcalidrawElement[]):ExcalidrawImageElement[] =>
|
||||
elements
|
||||
? elements.filter((element) =>
|
||||
element.type === "image" && element.customData?.mermaidText
|
||||
) as ExcalidrawImageElement[]
|
||||
: [];
|
||||
|
||||
export const getMermaidText = (element: ExcalidrawElement):string =>
|
||||
element.customData?.mermaidText;
|
||||
|
||||
export const shouldRenderMermaid = ():boolean => requireApiVersion("1.4.14");
|
||||
@@ -211,9 +211,8 @@ export const getFontDataURL = async (
|
||||
: "font/truetype";
|
||||
fontName = name ?? f.basename;
|
||||
dataURL = await getDataURL(ab, mimeType);
|
||||
fontDef = ` @font-face {font-family: "${fontName}";src: url("${dataURL}") format("${
|
||||
f.extension === "ttf" ? "truetype" : f.extension
|
||||
}");}`;
|
||||
fontDef = ` @font-face {font-family: "${fontName}";src: url("${dataURL}")}`;
|
||||
//format("${f.extension === "ttf" ? "truetype" : f.extension}");}`;
|
||||
const split = fontDef.split(";base64,", 2);
|
||||
fontDef = `${split[0]};charset=utf-8;base64,${split[1]}`;
|
||||
}
|
||||
@@ -352,11 +351,12 @@ export const getQuickImagePreview = async (
|
||||
export const embedFontsInSVG = (
|
||||
svg: SVGSVGElement,
|
||||
plugin: ExcalidrawPlugin,
|
||||
localOnly: boolean = false,
|
||||
): SVGSVGElement => {
|
||||
//replace font references with base64 fonts
|
||||
const includesVirgil =
|
||||
//replace font references with base64 fonts)
|
||||
const includesVirgil = !localOnly &&
|
||||
svg.querySelector("text[font-family^='Virgil']") != null;
|
||||
const includesCascadia =
|
||||
const includesCascadia = !localOnly &&
|
||||
svg.querySelector("text[font-family^='Cascadia']") != null;
|
||||
const includesLocalFont =
|
||||
svg.querySelector("text[font-family^='LocalFont']") != null;
|
||||
|
||||
@@ -414,4 +414,13 @@ div.excalidraw-draginfo {
|
||||
|
||||
.excalidraw-svg svg a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.excalidraw .Modal {
|
||||
background-color: initial;
|
||||
border: initial;
|
||||
max-width: initial;
|
||||
max-height: initial;
|
||||
width: initial;
|
||||
height: initial;
|
||||
}
|
||||
Reference in New Issue
Block a user