Compare commits

..

3 Commits

Author SHA1 Message Date
Zsolt Viczian
36ead43102 1.4.14 2021-11-28 20:54:51 +01:00
zsviczian
f61d000326 Update README.md 2021-11-28 07:52:44 +01:00
zsviczian
9bb254dc48 Update README.md 2021-11-28 07:42:21 +01:00
9 changed files with 155 additions and 32 deletions

View File

@@ -11,6 +11,7 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
|[![3 Groups](https://user-images.githubusercontent.com/14358394/125160323-96f89580-e17c-11eb-9bce-8eb1067a51bb.jpg)](https://youtu.be/QOL1KF7-kdc)|[![4 Stencil](https://user-images.githubusercontent.com/14358394/125160332-9f50d080-e17c-11eb-98e9-fec60fe147d9.jpg)](https://youtu.be/aSgcbfspvfo)|[![5 embedding](https://user-images.githubusercontent.com/14358394/125160341-a546b180-e17c-11eb-9de8-d87fdc844c9c.jpg)](https://youtu.be/MaJ5jJwBRWs)|
|[![6 Links](https://user-images.githubusercontent.com/14358394/125160346-aa0b6580-e17c-11eb-930b-4024807040d1.jpg)](https://youtu.be/MXzeCOEExNo)|[![7 Markdown](https://user-images.githubusercontent.com/14358394/125160354-b2fc3700-e17c-11eb-81af-9e71e461f6dd.jpg)](https://youtu.be/R0IAg0s-wQE)|[![8 Templates](https://user-images.githubusercontent.com/14358394/125160360-b8f21800-e17c-11eb-8bd8-79d4e3f6e92d.jpg)](https://youtu.be/ibdS7ykwpW4)|
|[![9 Excalidraw Automate](https://user-images.githubusercontent.com/14358394/125160367-bdb6cc00-e17c-11eb-92f1-6f59faea85fd.jpg)](https://youtu.be/VRZVujfVab0)|[![10 Miscellaneous](https://user-images.githubusercontent.com/14358394/125160374-c3141680-e17c-11eb-8cc2-dfaffd903d15.jpg)](https://youtu.be/D1iBYo1_jjc)|[![Image Elements](https://user-images.githubusercontent.com/14358394/138607067-ccb62f92-48a4-4880-ac6e-68c1bf86ac2c.png)](https://www.youtube.com/watch?v=_c_0zpBJ4Xc&)|
|[![LaTex Demo](https://user-images.githubusercontent.com/14358394/143732412-1c65227e-4381-406d-847a-b001ab3506ca.jpg)](https://youtu.be/r08wk-58DPk)|[![markdown embeds](https://user-images.githubusercontent.com/14358394/143732440-90bfa029-8615-462e-ada3-c903d71a82c9.jpg)](https://youtu.be/tsecSfnTMow)||
# Key features
- The plugin aims to integrate Excalidraw seamlessly into Obsidian including Command Palette actions, File Explorer features, Option Menu commands, and the Ribbon Button.
@@ -58,6 +59,14 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release.
- `excalidraw-link-prefix: "📍"` preview prefix for internal links
- `excalidraw-url-prefix: "🌐"` preview prefix for external links
- `excalidraw-link-brackets: true|false` whether or not to display brackets around links in preview
- Embed complete markdown files into your drawings
- Drag from the desired file from the Obsidian file explorer and hold down CTRL/CMD while dropping the file onto the canvas.
- Use the command palette action: `Insert markdown file from vault`
- Use custom woff, woff2 or TTF font to display the document, you can set the default font to use under Excalidraw Settings.
- You can control appearance of the embedded markdown file on a file by file bases by adding the following front matter keys to your markdown document:
- `excalidraw-font: Virgil|Cascadia|font_file_name.extension`
- `excalidraw-font-color: css-color-name|#HEXcolor|any-other-html-standard-format`, you can find css color names [here](https://www.w3schools.com/colors/colors_names.asp).
- Switch to markdown view or use CTRL/CMD+ALT/OPT click on the image to edit properties of the embed: [[filename#^blockref|WIDTHxMAXHEIGHT]]
- Includes full [QuickAdd](https://github.com/chhoumann/quickadd), [Templater](https://silentvoid13.github.io/Templater/) and [Dataview](https://blacksmithgu.github.io/obsidian-dataview/docs/api/intro/) support through ExcalidrawAutomate. Check out the [detailed help + examples](https://zsviczian.github.io/obsidian-excalidraw-plugin/). I also have a [YouTube ExcalidrawAutomate Playlist](https://www.youtube.com/playlist?list=PL6mqgtMZ4NP1IR4nXxSlMA4PA5E-qpyHZ) with lots of examples.
- REQUIRES AN OBSIDIAN SYNC SUBSCRIPTION: Full drawing file history and synchronization between devices
- Multilanguage support: if you'd like to help out by translating the plugin, please get in contact with me.

View File

@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "1.4.13",
"version": "1.4.14",
"minAppVersion": "0.12.16",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",

View File

@@ -1,16 +1,14 @@
import { FileId } from "@zsviczian/excalidraw/types/element/types";
import { BinaryFileData, DataURL } from "@zsviczian/excalidraw/types/types";
import { link } from "fs";
import { App, MarkdownRenderer, Notice, TFile } from "obsidian";
import { CASCADIA_FONT, fileid, FRONTMATTER_KEY_CSS, FRONTMATTER_KEY_FONT, FRONTMATTER_KEY_FONTCOLOR, IMAGE_TYPES, nanoid, VIRGIL_FONT } from "./constants";
import { CASCADIA_FONT, fileid, FRONTMATTER_KEY_FONT, FRONTMATTER_KEY_FONTCOLOR, FRONTMATTER_KEY_MD_STYLE, IMAGE_TYPES, nanoid, VIRGIL_FONT } from "./constants";
import { createSVG } from "./ExcalidrawAutomate";
import { ExcalidrawData, getTransclusion } from "./ExcalidrawData";
import ExcalidrawView, { ExportSettings } from "./ExcalidrawView";
import { ExportSettings } from "./ExcalidrawView";
import { t } from "./lang/helpers";
import de from "./lang/locale/de";
import { tex2dataURL } from "./LaTeX";
import ExcalidrawPlugin from "./main";
import {debug, errorlog, getImageSize, getLinkParts, LinkParts, svgToBase64 } from "./Utils";
import {errorlog, getImageSize, getLinkParts, LinkParts, svgToBase64 } from "./Utils";
export declare type MimeType = "image/svg+xml" | "image/png" | "image/jpeg" | "image/gif" | "application/octet-stream";
export type FileData = BinaryFileData & {
@@ -282,12 +280,14 @@ const getSVGData = async (app: App, file: TFile): Promise<DataURL> => {
}
const convertMarkdownToSVG = async (plugin: ExcalidrawPlugin, file: TFile, linkParts: LinkParts): Promise<DataURL> => {
//const text = await plugin.app.vault.cachedRead(file);
//1.
//get the markdown text
const [text,line] = await getTransclusion(linkParts,plugin.app,file);
const fileCache = plugin.app.metadataCache.getFileCache(file);
//2.
//get styles
const fileCache = plugin.app.metadataCache.getFileCache(file);
let fontName:string;
let fontDef:string;
let font = plugin.settings.mdFont;
@@ -313,31 +313,93 @@ const convertMarkdownToSVG = async (plugin: ExcalidrawPlugin, file: TFile, linkP
}
const fontColor = fileCache?.frontmatter ? fileCache.frontmatter[FRONTMATTER_KEY_FONTCOLOR] : plugin.settings.mdFontColor;
//construct SVG
const div = createDiv();
div.setAttribute("xmlns","http://www.w3.org/1999/xhtml");
div.style.fontFamily = fontName;
if(fontColor) div.style.color = fontColor;
div.style.fontSize = "initial";
await MarkdownRenderer.renderMarkdown(text,div,file.path,plugin);
div.querySelectorAll(":scope > *[class^='frontmatter']").forEach((el)=>div.removeChild(el));
//brute force to swap <a> to <u> because links anyway don't work when the foreignObject is
//encapsulated in an img element. <a> does not render with an underline, <u> will.
const xml = (new XMLSerializer().serializeToString(div)).replaceAll("<a ","<u ").replaceAll("</a>","</u>");
let svgStyle = ' width="'+linkParts.width+'px" height="100%"';
let style = fileCache?.frontmatter ? (fileCache.frontmatter[FRONTMATTER_KEY_MD_STYLE]??"") : "";
let frontmatterCSSisAfile = false;
if(style && style!="") {
const f = plugin.app.metadataCache.getFirstLinkpathDest(style,file.path);
if(f) {
style = await plugin.app.vault.read(f);
frontmatterCSSisAfile = true;
}
}
if(!frontmatterCSSisAfile && plugin.settings.mdCSS && plugin.settings.mdCSS!="") {
const f = plugin.app.metadataCache.getFirstLinkpathDest(plugin.settings.mdCSS,file.path);
if(f) {
style += "\n"+await plugin.app.vault.read(f);
}
}
//3.
//SVG helper functions
//the SVG will first have ~infinite height. After sizing this will be reduced
let svgStyle = ' width="'+linkParts.width+'px" height="100000"';
let foreignObjectStyle = ' width="'+linkParts.width+'px" height="100%"';
const svg = () => '<svg xmlns="http://www.w3.org/2000/svg"'+svgStyle+'>'
const svg = (xml:string,style?:string) =>
'<svg xmlns="http://www.w3.org/2000/svg"'+svgStyle+'>'
+ (style?'<style>'+style+'</style>':'')
+ '<foreignObject x="0" y="0"'+foreignObjectStyle+'>'
+ xml
+ '</foreignObject><defs><style>'
+ fontDef
+ '</style></defs></svg>';
//4.
//create document div - this will be the contents of the foreign object
const mdDIV = createDiv();
mdDIV.setAttribute("xmlns","http://www.w3.org/1999/xhtml");
mdDIV.setAttribute("class","excalidraw-md-host");
mdDIV.setAttribute("style",style);
mdDIV.style.fontFamily = fontName;
mdDIV.style.overflow = "auto";
mdDIV.style.display = "block";
if(fontColor) mdDIV.style.color = fontColor;
await MarkdownRenderer.renderMarkdown(text,mdDIV,file.path,plugin);
mdDIV.querySelectorAll(":scope > *[class^='frontmatter']").forEach((el)=>mdDIV.removeChild(el));
//this is a brute force approach to replace anchors with spans for better formatting
//mdDIV.innerHTML = mdDIV.innerHTML.replaceAll("<a ","<u ").replaceAll("</a>","</u>");
//5.1
//get SVG size.
//First I need to create a fully self contained copy of the document to convert
//blank styles into inline styles using computedStyle
const iframeHost = document.body.createDiv();
iframeHost.style.display = "none";
const iframe = iframeHost.createEl("iframe");
const iframeDoc = iframe.contentWindow.document;
if(style) {
const styleEl = iframeDoc.createElement("style");
styleEl.type = "text/css";
styleEl.innerHTML = style;
iframeDoc.head.appendChild(styleEl);
}
const stylingDIV = iframeDoc.importNode(mdDIV,true)
iframeDoc.body.appendChild(stylingDIV);
iframeDoc.body.querySelectorAll("*").forEach((el:HTMLElement)=>{
const elementStyle = el.style;
const computedStyle = window.getComputedStyle(el);
let style = "";
for (const prop in elementStyle) {
if (elementStyle.hasOwnProperty(prop)) {
style += prop + ": " + computedStyle[prop] + ";";
}
}
el.setAttribute("style",style);
});
const xmlINiframe = (new XMLSerializer().serializeToString(stylingDIV))
document.body.removeChild(iframeHost);
//5.2
//get SVG size
const parser = new DOMParser();
const doc = parser.parseFromString(svg(),"image/svg+xml");
const doc = parser.parseFromString(svg(xmlINiframe),"image/svg+xml");
const svgEl = doc.firstElementChild;
const host = createDiv();
host.appendChild(svgEl);
@@ -349,7 +411,40 @@ const convertMarkdownToSVG = async (plugin: ExcalidrawPlugin, file: TFile, linkP
//finalize SVG
svgStyle = ' width="'+linkParts.width+'px" height="'+svgHeight+'px"';
foreignObjectStyle = ' width="'+linkParts.width+'px" height="'+svgHeight+'px"';
return svgToBase64(svg()) as DataURL;
const xml = (new XMLSerializer().serializeToString(mdDIV))
const finalSVG = svg(xml,style);
plugin.ea.mostRecentMarkdownSVG = parser.parseFromString(finalSVG,"image/svg+xml").firstElementChild as SVGSVGElement;
return svgToBase64(finalSVG) as DataURL;
}
const styleSandbox = async (style:string,fontName:string,fontColor:string, text:string,path:string,plugin:ExcalidrawPlugin) => {
const host = document.body.createDiv();
host.style.display = "none";
const iframe = host.createEl("iframe");
const doc = iframe.contentWindow.document;
if(style) {
const styleEl = doc.createElement("style");
styleEl.type = "text/css";
styleEl.innerHTML = style;
doc.head.appendChild(styleEl);
}
const div = createDiv("div");
doc.body.querySelectorAll("*").forEach((el:HTMLElement)=>{
const elementStyle = el.style;
const computedStyle = window.getComputedStyle(el);
let style = "";
for (const prop in elementStyle) {
if (elementStyle.hasOwnProperty(prop)) {
style += prop + ": " + computedStyle[prop] + ";";
}
}
el.setAttribute("style",style);
});
const xml = (new XMLSerializer().serializeToString(div))
document.body.removeChild(host);
return xml;
}
const getDataURL = async (file: ArrayBuffer,mimeType: string): Promise<DataURL> => {

View File

@@ -7,7 +7,6 @@ import {
FileId,
} from "@zsviczian/excalidraw/types/element/types";
import {
MarkdownRenderer,
normalizePath,
TFile
} from "obsidian"
@@ -169,7 +168,7 @@ export interface ExcalidrawAutomate {
view: ExcalidrawView, //the excalidraw view receiving the drop
pointerPosition: {x:number, y:number} //the pointer position on canvas at the time of drop
}):boolean;
renderMarkdown(text:string,el:HTMLElement):Promise<void>;
mostRecentMarkdownSVG:SVGSVGElement; //Markdown renderer will drop a copy of the most recent SVG here for debugging purposes
}
declare let window: any;
@@ -750,9 +749,7 @@ export async function initExcalidrawAutomate(plugin: ExcalidrawPlugin):Promise<E
return await this.targetView.addElements(elements,repositionToCursor,save,this.imagesDict);
},
onDropHook:null,
async renderMarkdown(text:string,el:HTMLElement):Promise<void> {
await MarkdownRenderer.renderMarkdown(text,el,'',this.plugin);
}
mostRecentMarkdownSVG:null,
};
await initFonts();
return window.ExcalidrawAutomate;

View File

@@ -15,7 +15,7 @@ export const FRONTMATTER_KEY_CUSTOM_LINK_BRACKETS = "excalidraw-link-brackets";
export const FRONTMATTER_KEY_DEFAULT_MODE = "excalidraw-default-mode";
export const FRONTMATTER_KEY_FONT = "excalidraw-font";
export const FRONTMATTER_KEY_FONTCOLOR = "excalidraw-font-color";
export const FRONTMATTER_KEY_CSS = "excalidraw-css-file";
export const FRONTMATTER_KEY_MD_STYLE = "excalidraw-css";
export const VIEW_TYPE_EXCALIDRAW = "excalidraw";
export const ICON_NAME = "excalidraw-icon";
export const MAX_COLORS = 5;

View File

@@ -143,6 +143,12 @@ export default {
MD_DEFAULT_COLOR_NAME: "The default font color to use for embedded markdown files.",
MD_DEFAULT_COLOR_DESC: 'Set this to allowed css color names e.g. "steelblue" (https://www.w3schools.com/colors/colors_names.asp), or a valid hexadecimal color e.g. "#e67700". ' +
'You can override this setting by adding the following frontmatter-key to the embedded markdown file: "excalidraw-font-color: color_name_or_rgbhex"',
MD_CSS_NAME: "CSS file",
MD_CSS_DESC: "Filename of the CSS to apply to markdown embeds. Provide the filename with extension (e.g. 'md-embed.css'). Nota bene, the filename may also be a plain " +
"markdown file as well, just make sure the content is written using valid css syntax (e.g. 'md-embed-css.md') will work just as well. " +
"The generated HTML that is embedded into the image is the same as normal rendered documents in Obsidian. " +
"Setting the font-family in the css is currently not supported; it should be set separately using the setting above. " +
'You can override this css setting by adding the following frontmatter-key to the embedded markdown file: "excalidraw-css: css_file_in_valut".',
EMBED_HEAD: "Embed & Export",
EMBED_PREVIEW_SVG_NAME: "Display SVG in markdown preview",
EMBED_PREVIEW_SVG_DESC: "The default is to display drawings as SVG images in the markdown preview. Turning this feature off, the markdown preview will display the drawing as an embedded PNG image.",

View File

@@ -460,6 +460,8 @@ export default class ExcalidrawPlugin extends Plugin {
if(!this.settings.matchThemeTrigger) return;
//@ts-ignore
if(m[0]?.oldValue === m[0]?.target?.getAttribute("class")) return;
//@ts-ignore
if(m[0]?.oldValue?.includes("theme-dark") === m[0]?.target?.classList?.contains("theme-dark")) return;
const theme = isObsidianThemeDark() ? "dark":"light";
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
leaves.forEach((leaf:WorkspaceLeaf)=> {

View File

@@ -56,6 +56,7 @@ export interface ExcalidrawSettings {
mdSVGmaxHeight: number,
mdFont: string,
mdFontColor: string,
mdCSS: string,
}
export const DEFAULT_SETTINGS: ExcalidrawSettings = {
@@ -109,6 +110,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
mdSVGmaxHeight: 800,
mdFont: "Virgil",
mdFontColor: "Black",
mdCSS: ""
}
export class ExcalidrawSettingTab extends PluginSettingTab {
@@ -478,6 +480,18 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
this.applySettingsUpdate(true);
}));
new Setting(containerEl)
.setName(t("MD_CSS_NAME"))
.setDesc(t("MD_CSS_DESC"))
.addText(text => text
.setPlaceholder("filename of css file in vault")
.setValue(this.plugin.settings.mdCSS)
.onChange((value) => {
this.requestReloadDrawings=true;
this.plugin.settings.mdCSS = value;
this.applySettingsUpdate(true);
}));
this.containerEl.createEl('h1', {text: t("EMBED_HEAD")});

View File

@@ -1,4 +1,4 @@
{
"1.4.13": "0.12.16",
"1.4.14": "0.12.16",
"1.4.2": "0.11.13"
}