diff --git a/README.md b/README.md index 2873bb5..53d7dc5 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release. |[![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)|[![markdownAdvanced](https://user-images.githubusercontent.com/14358394/143783906-15cee494-c6d5-4495-a2ca-74634e4e7355.jpg)](https://youtu.be/K6qZkTz8GHs)| |[![Script Engine](https://user-images.githubusercontent.com/14358394/145684531-8d9c2992-59ac-4ebc-804a-4cce1777ded2.jpg)](https://youtu.be/hePJcObHIso)|[![sticky notes thumbnail](https://user-images.githubusercontent.com/14358394/147283367-e5689385-ea51-4983-81a3-04d810d39f62.jpg)](https://youtu.be/NOuddK6xrr8)|[![plugin store](https://user-images.githubusercontent.com/14358394/147889174-6c306d0d-2d29-46cc-a53f-3f0013cf14de.jpg)](https://youtu.be/lzYdOQ6z8F0)| +|[![fourtfont](https://user-images.githubusercontent.com/14358394/149659524-2a4e0a24-40c9-4e66-a6b1-c92f3b88ecd5.jpg)](https://youtu.be/eKFmrSQhFA4)||| # Key features diff --git a/docs/API/attributes_functions_overview.md b/docs/API/attributes_functions_overview.md index 36f1fed..700bb17 100644 --- a/docs/API/attributes_functions_overview.md +++ b/docs/API/attributes_functions_overview.md @@ -17,7 +17,7 @@ export interface ExcalidrawAutomate { roughness: number; opacity: number; strokeSharpness: StrokeSharpness; //type StrokeSharpness = "round" | "sharp" - fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia + fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont fontSize: number; textAlign: string; //"left"|"right"|"center" verticalAlign: string; //"top"|"bottom"|"middle" :for future use, has no effect currently diff --git a/manifest.json b/manifest.json index aea696e..0733705 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-excalidraw-plugin", "name": "Excalidraw", - "version": "1.5.16", + "version": "1.5.17", "minAppVersion": "0.12.16", "description": "An Obsidian plugin to edit and view Excalidraw drawings", "author": "Zsolt Viczian", diff --git a/package.json b/package.json index 7a3a9d0..5182511 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "author": "", "license": "MIT", "dependencies": { - "@zsviczian/excalidraw": "0.10.0-obsidian-33", + "@zsviczian/excalidraw": "0.10.0-obsidian-34", "monkey-around": "^2.2.0", "react": "^17.0.2", "react-dom": "^17.0.2", diff --git a/src/EmbeddedFileLoader.ts b/src/EmbeddedFileLoader.ts index 774e273..fb3ae7c 100644 --- a/src/EmbeddedFileLoader.ts +++ b/src/EmbeddedFileLoader.ts @@ -19,6 +19,8 @@ import { tex2dataURL } from "./LaTeX"; import ExcalidrawPlugin from "./main"; import { errorlog, + getDataURL, + getFontDataURL, getImageSize, getLinkParts, LinkParts, @@ -397,25 +399,9 @@ const convertMarkdownToSVG = async ( fontDef = ""; break; default: - const f = plugin.app.metadataCache.getFirstLinkpathDest( - fontName, - file.path, - ); - if (f) { - const ab = await plugin.app.vault.readBinary(f); - const mimeType = f.extension.startsWith("woff") - ? "application/font-woff" - : "font/truetype"; - fontName = f.basename; - fontDef = ` @font-face {font-family: "${fontName}";src: url("${await getDataURL( - ab, - mimeType, - )}") format("${f.extension === "ttf" ? "truetype" : f.extension}");}`; - const split = fontDef.split(";base64,", 2); - fontDef = `${split[0]};charset=utf-8;base64,${split[1]}`; - } else { - fontDef = ""; - } + const font = await getFontDataURL(plugin.app,fontName,file.path); + fontDef = font.fontDef; + fontName = font.fontName; } const fontColor = fileCache?.frontmatter @@ -552,21 +538,6 @@ const convertMarkdownToSVG = async ( return svgToBase64(finalSVG) as DataURL; }; -const getDataURL = async ( - file: ArrayBuffer, - mimeType: string, -): Promise => { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => { - const dataURL = reader.result as DataURL; - resolve(dataURL); - }; - reader.onerror = (error) => reject(error); - reader.readAsDataURL(new Blob([new Uint8Array(file)], { type: mimeType })); - }); -}; - const generateIdFromFile = async (file: ArrayBuffer): Promise => { let id: FileId; try { diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index 2caf980..bf9e661 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -52,7 +52,7 @@ export interface ExcalidrawAutomate { roughness: number; opacity: number; strokeSharpness: StrokeSharpness; //type StrokeSharpness = "round" | "sharp" - fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia + fontFamily: number; //1: Virgil, 2:Helvetica, 3:Cascadia, 4:LocalFont fontSize: number; textAlign: string; //"left"|"right"|"center" verticalAlign: string; //"top"|"bottom"|"middle" :for future use, has no effect currently @@ -289,14 +289,17 @@ export async function initExcalidrawAutomate( setFontFamily(val: number) { switch (val) { case 1: - this.style.fontFamily = 1; - return getFontFamily(1); + this.style.fontFamily = 4; + return getFontFamily(4); case 2: this.style.fontFamily = 2; return getFontFamily(2); - default: + case 3: this.style.strokeSharpness = 3; return getFontFamily(3); + default: + this.style.strokeSharpness = 1; + return getFontFamily(1); } }, setTheme(val: number) { @@ -1333,6 +1336,8 @@ function getFontFamily(id: number) { return "Helvetica, Segoe UI Emoji"; case 3: return "Cascadia, Segoe UI Emoji"; + case 4: + return "LocalFont"; } } diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index 89459da..1431536 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -152,9 +152,9 @@ export default class ExcalidrawView extends TextFileView { private preventReload: boolean = true; public compatibilityMode: boolean = false; //store key state for view mode link resolution - private ctrlKeyDown = false; + /*private ctrlKeyDown = false; private shiftKeyDown = false; - private altKeyDown = false; + private altKeyDown = false;*/ //https://stackoverflow.com/questions/27132796/is-there-any-javascript-event-fired-when-the-on-screen-keyboard-on-mobile-safari private isEditingText: boolean = false; @@ -823,6 +823,11 @@ export default class ExcalidrawView extends TextFileView { ? om.zenModeEnabled : this.excalidrawAPI.getAppState().zenModeEnabled; //debug({where:"ExcalidrawView.loadDrawing",file:this.file.name,dataTheme:excalidrawData.appState.theme,before:"updateScene"}) + this.excalidrawAPI.setLocalFont( + this.plugin.fourthFontDataURL, + this.plugin.settings.experimentalEnableFourthFont + ); + this.excalidrawAPI.updateScene({ elements: excalidrawData.elements, appState: { @@ -1070,6 +1075,11 @@ export default class ExcalidrawView extends TextFileView { this.excalidrawAPI = api; //console.log({where:"ExcalidrawView.React.ReadyPromise"}); //debug({where:"ExcalidrawView.React.useEffect",file:this.file.name,before:"this.loadSceneFiles"}); + this.excalidrawAPI.setLocalFont( + this.plugin.fourthFontDataURL, + this.plugin.settings.experimentalEnableFourthFont + ); + this.loadSceneFiles(); this.updateContainerSize(null, true); }); @@ -1517,8 +1527,8 @@ export default class ExcalidrawView extends TextFileView { const event = new MouseEvent("click", { ctrlKey: true, metaKey: true, - shiftKey: this.shiftKeyDown, - altKey: this.altKeyDown, + shiftKey: this.plugin.shiftKeyDown, + altKey: this.plugin.altKeyDown, }); this.handleLinkClick(this, event); selectedTextElement = null; @@ -1528,8 +1538,8 @@ export default class ExcalidrawView extends TextFileView { const event = new MouseEvent("click", { ctrlKey: true, metaKey: true, - shiftKey: this.shiftKeyDown, - altKey: this.altKeyDown, + shiftKey: this.plugin.shiftKeyDown, + altKey: this.plugin.altKeyDown, }); this.handleLinkClick(this, event); selectedImageElement = null; @@ -1538,6 +1548,73 @@ export default class ExcalidrawView extends TextFileView { let mouseEvent: any = null; + const showHoverPreview = () => { + let linktext = ""; + const selectedElement = getTextElementAtPointer(currentPosition); + if (!selectedElement || !selectedElement.text) { + const selectedImgElement = + getImageElementAtPointer(currentPosition); + if (!selectedImgElement || !selectedImgElement.fileId) { + return; + } + if (!this.excalidrawData.hasFile(selectedImgElement.fileId)) { + return; + } + const ef = this.excalidrawData.getFile( + selectedImgElement.fileId, + ); + const ref = ef.linkParts.ref + ? `#${ef.linkParts.isBlockRef ? "^" : ""}${ef.linkParts.ref}` + : ""; + linktext = + this.excalidrawData.getFile(selectedImgElement.fileId).file + .path + ref; + } else { + const text: string = + this.textMode === TextMode.parsed + ? this.excalidrawData.getRawText(selectedElement.id) + : selectedElement.text; + + if (!text) { + return; + } + if (text.match(REG_LINKINDEX_HYPERLINK)) { + return; + } + + const parts = REGEX_LINK.getRes(text).next(); + if (!parts.value) { + return; + } + linktext = REGEX_LINK.getLink(parts); //parts.value[2] ? parts.value[2]:parts.value[6]; + if (linktext.match(REG_LINKINDEX_HYPERLINK)) { + return; + } + } + + this.plugin.hover.linkText = linktext; + this.plugin.hover.sourcePath = this.file.path; + hoverPreviewTarget = this.contentEl; //e.target; + this.app.workspace.trigger("hover-link", { + event: mouseEvent, + source: VIEW_TYPE_EXCALIDRAW, + hoverParent: hoverPreviewTarget, + targetEl: hoverPreviewTarget, + linktext: this.plugin.hover.linkText, + sourcePath: this.plugin.hover.sourcePath, + }); + hoverPoint = currentPosition; + if (this.isFullscreen()) { + const self = this; + setTimeout(() => { + const popover = document.body.querySelector("div.popover"); + if (popover) { + self.contentEl.append(popover); + } + }, 100); + } + } + const excalidrawDiv = React.createElement( "div", { @@ -1554,83 +1631,20 @@ export default class ExcalidrawView extends TextFileView { this.exitFullscreen(); } + /* this.ctrlKeyDown = e[CTRL_OR_CMD]; //.ctrlKey||e.metaKey; this.shiftKeyDown = e.shiftKey; - this.altKeyDown = e.altKey; + this.altKeyDown = e.altKey;*/ if (e[CTRL_OR_CMD] && !e.shiftKey && !e.altKey) { - //.ctrlKey||e.metaKey) && !e.shiftKey && !e.altKey) { - let linktext = ""; - const selectedElement = getTextElementAtPointer(currentPosition); - if (!selectedElement || !selectedElement.text) { - const selectedImgElement = - getImageElementAtPointer(currentPosition); - if (!selectedImgElement || !selectedImgElement.fileId) { - return; - } - if (!this.excalidrawData.hasFile(selectedImgElement.fileId)) { - return; - } - const ef = this.excalidrawData.getFile( - selectedImgElement.fileId, - ); - const ref = ef.linkParts.ref - ? `#${ef.linkParts.isBlockRef ? "^" : ""}${ef.linkParts.ref}` - : ""; - linktext = - this.excalidrawData.getFile(selectedImgElement.fileId).file - .path + ref; - } else { - const text: string = - this.textMode === TextMode.parsed - ? this.excalidrawData.getRawText(selectedElement.id) - : selectedElement.text; - - if (!text) { - return; - } - if (text.match(REG_LINKINDEX_HYPERLINK)) { - return; - } - - const parts = REGEX_LINK.getRes(text).next(); - if (!parts.value) { - return; - } - linktext = REGEX_LINK.getLink(parts); //parts.value[2] ? parts.value[2]:parts.value[6]; - if (linktext.match(REG_LINKINDEX_HYPERLINK)) { - return; - } - } - - this.plugin.hover.linkText = linktext; - this.plugin.hover.sourcePath = this.file.path; - hoverPreviewTarget = this.contentEl; //e.target; - this.app.workspace.trigger("hover-link", { - event: mouseEvent, - source: VIEW_TYPE_EXCALIDRAW, - hoverParent: hoverPreviewTarget, - targetEl: hoverPreviewTarget, - linktext: this.plugin.hover.linkText, - sourcePath: this.plugin.hover.sourcePath, - }); - hoverPoint = currentPosition; - if (this.isFullscreen()) { - const self = this; - setTimeout(() => { - const popover = document.body.querySelector("div.popover"); - if (popover) { - self.contentEl.append(popover); - } - }, 100); - } + showHoverPreview(); } }, - onKeyUp: (e: any) => { +/* onKeyUp: (e: any) => { this.ctrlKeyDown = e[CTRL_OR_CMD]; //.ctrlKey||e.metaKey; this.shiftKeyDown = e.shiftKey; this.altKeyDown = e.altKey; - }, + },*/ onClick: (e: MouseEvent): any => { if (!e[CTRL_OR_CMD]) { return; @@ -1699,7 +1713,7 @@ export default class ExcalidrawView extends TextFileView { blockOnMouseButtonDown = true; //ctrl click - if (this.ctrlKeyDown) { + if (this.plugin.ctrlKeyDown) { handleLinkClick(); return; } @@ -1715,6 +1729,9 @@ export default class ExcalidrawView extends TextFileView { if (p.button === "up") { blockOnMouseButtonDown = false; } + if (this.plugin.ctrlKeyDown) { + showHoverPreview(); + } }, onChange: (et: ExcalidrawElement[], st: AppState) => { viewModeEnabled = st.viewModeEnabled; diff --git a/src/MarkdownPostProcessor.ts b/src/MarkdownPostProcessor.ts index 6896296..13e456f 100644 --- a/src/MarkdownPostProcessor.ts +++ b/src/MarkdownPostProcessor.ts @@ -312,7 +312,10 @@ const tmpObsidianWYSIWYG = async ( if(hasHeight) attr.fheight = internalEmbedDiv.getAttribute("height"); const alt = internalEmbedDiv.getAttribute("alt"); - const hasAttr = alt && alt!=="" && alt!==basename; + const hasAttr = alt + && alt !== "" + && alt !== basename + && alt !== internalEmbedDiv.getAttribute("src"); if(hasAttr) { //1:width, 2:height, 3:style 1 2 3 const parts = alt.match(/(\d*%?)x?(\d*%?)\|?(.*)/); diff --git a/src/Utils.ts b/src/Utils.ts index 43ed118..4f7d075 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -3,13 +3,14 @@ import { App, normalizePath, Notice, + request, TAbstractFile, TFolder, Vault, WorkspaceLeaf, } from "obsidian"; import { Random } from "roughjs/bin/math"; -import { Zoom } from "@zsviczian/excalidraw/types/types"; +import { DataURL, Zoom } from "@zsviczian/excalidraw/types/types"; import { CASCADIA_FONT, REG_BLOCK_REF_CLEAN, VIRGIL_FONT } from "./constants"; import ExcalidrawPlugin from "./main"; import { ExcalidrawElement } from "@zsviczian/excalidraw/types/element/types"; @@ -34,14 +35,35 @@ export const checkExcalidrawVersion = async (app:App) => { versionUpdateChecked = true; //@ts-ignore const manifest = app.plugins.manifests["obsidian-excalidraw-plugin"]; - //@ts-ignore - const latestVersion = await app.plugins.getLatestVersion("obsidian-excalidraw-plugin",manifest) - if(latestVersion>manifest.version) { - new Notice(`A newer version of Excalidraw is available in Community Plugins. You are using ${manifest.version}. The latest version is ${latestVersion}`); + + try { + const gitAPIrequest = async () => { + return JSON.parse(await request({ + url: `https://api.github.com/repos/zsviczian/obsidian-excalidraw-plugin/releases?per_page=5&page=1`, + })) + } + + const latestVersion = ((await gitAPIrequest()) + .map((el:any) => { + return { + version: el.tag_name, + published: new Date(el.published_at), + }; + }) + .filter((el:any) => el.version.match(/^\d+\.\d+\.\d+$/)) + .sort((el1:any,el2:any)=>el2.published-el1.published))[0].version; + + if(latestVersion>manifest.version) { + new Notice(`A newer version of Excalidraw is available in Community Plugins.\n\nYou are using ${manifest.version}.\nThe latest is ${latestVersion}`); + } + } catch(e) { + errorlog({where:"Utils/checkExcalidrawVersion", error:e}); } setTimeout(()=>versionUpdateChecked=false,28800000);//reset after 8 hours } + + /** * Splits a full path including a folderpath and a filename into separate folderpath and filename components * @param filepath @@ -281,6 +303,53 @@ export const getNewOrAdjacentLeaf = ( return plugin.app.workspace.createLeafBySplit(leaf); }; +export const getDataURL = async ( + file: ArrayBuffer, + mimeType: string, +): Promise => { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => { + const dataURL = reader.result as DataURL; + resolve(dataURL); + }; + reader.onerror = (error) => reject(error); + reader.readAsDataURL(new Blob([new Uint8Array(file)], { type: mimeType })); + }); +}; + +export const getFontDataURL = async ( + app:App, + fontFileName: string, + sourcePath: string, + name?:string +):Promise<{fontDef: string, fontName: string, dataURL: string}> => { + let fontDef:string = ""; + let fontName = ""; + let dataURL = ""; + const f = app.metadataCache.getFirstLinkpathDest( + fontFileName, + sourcePath, + ); + if (f) { + const ab = await app.vault.readBinary(f); + const mimeType = f.extension.startsWith("woff") + ? "application/font-woff" + : "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}");}`; + const split = fontDef.split(";base64,", 2); + fontDef = `${split[0]};charset=utf-8;base64,${split[1]}`; + } + return {fontDef,fontName,dataURL}; +} + export const svgToBase64 = (svg: string): string => { return `data:image/svg+xml;base64,${btoa( unescape(encodeURIComponent(svg.replaceAll(" ", " "))), diff --git a/src/constants.ts b/src/constants.ts index b7956d3..6399db6 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,4 @@ import { customAlphabet } from "nanoid"; - //This is only for backward compatibility because an early version of obsidian included an encoding to avoid fantom links from littering Obsidian graph view export function JSON_parse(x: string): any { return JSON.parse(x.replaceAll("[", "[")); diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index 523d6ed..f2ccf23 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -275,6 +275,15 @@ export default { LIVEPREVIEW_DESC: "Turn this on to support image embedding styles such as ![[drawing|width|style]] in live preview editing mode. " + "The setting will not effect the currently open documents. You need close the open documents and re-open them for the change " + "to take effect.", + ENABLE_FOURTH_FONT_NAME: "Enable fourth font option", + ENABLE_FOURTH_FONT_DESC: "By turning this on, you will see a fourth font button on the properties panel for text elements. " + + "Files that use this fourth font will (partly) lose their paltform independence. " + + "Depending on the cutom font set in settings, they will look differently when loaded in another vault, or at a later time. " + + "Also the 4th font will display as system default font on excalidraw.com, or other Excalidraw versions.", + FOURTH_FONT_NAME: "Forth font file", + FOURTH_FONT_DESC: "Select a .ttf, .woff or .woff2 font file from your vault to use as the fourth font. " + + "If no file is selected excalidraw will use the Virgil font by default.", + //openDrawings.ts SELECT_FILE: "Select a file then press enter.", diff --git a/src/main.ts b/src/main.ts index 42b21f4..2298782 100644 --- a/src/main.ts +++ b/src/main.ts @@ -39,6 +39,7 @@ import { CTRL_OR_CMD, SCRIPT_INSTALL_CODEBLOCK, SCRIPT_INSTALL_FOLDER, + VIRGIL_FONT, } from "./constants"; import ExcalidrawView, { ExportSettings, TextMode } from "./ExcalidrawView"; import { getMarkdownDrawingSection } from "./ExcalidrawData"; @@ -67,6 +68,7 @@ import { embedFontsInSVG, errorlog, getAttachmentsFolderAndFilePath, + getFontDataURL, getIMGFilename, getIMGPathFromExcalidrawFile, getNewUniqueFilepath, @@ -120,6 +122,7 @@ export default class ExcalidrawPlugin extends Plugin { public mathjax: any = null; private mathjaxDiv: HTMLDivElement = null; public scriptEngine: ScriptEngine; + public fourthFontDataURL: string = VIRGIL_FONT; constructor(app: App, manifest: PluginManifest) { super(app, manifest); @@ -155,6 +158,7 @@ export default class ExcalidrawPlugin extends Plugin { this.experimentalFileTypeDisplayToggle(this.settings.experimentalFileType); this.registerCommands(); this.registerEventListeners(); + this.initializeFourthFont(); //inspiration taken from kanban: //https://github.com/mgmeyers/obsidian-kanban/blob/44118e25661bff9ebfe54f71ae33805dc88ffa53/src/main.ts#L267 @@ -186,6 +190,31 @@ export default class ExcalidrawPlugin extends Plugin { }); } + public initializeFourthFont() { + this.app.workspace.onLayoutReady(async() => { + const font = (await getFontDataURL(this.app,this.settings.experimantalFourthFont,"","LocalFont")).dataURL; + this.fourthFontDataURL = font === "" ? VIRGIL_FONT : font; + + const newStylesheet = document.createElement("style"); + newStylesheet.id = "local-font-stylesheet"; + newStylesheet.textContent = ` + @font-face { + font-family: 'LocalFont'; + src: url("${font}"); + font-display: swap; + } + `; + // replace the old local font