Compare commits

...

4 Commits

Author SHA1 Message Date
zsviczian 491eb83d35 convert app.isMobile to DEVICE.isMobile, fix CropImage angle!==0 2024-04-30 19:31:00 +02:00
zsviczian 35cf0802d1 2.1.6 2024-04-23 22:19:49 +02:00
zsviczian f768548f60 2.1.5 2024-04-22 20:10:21 +02:00
zsviczian 37789f9907 Update issue templates 2024-04-20 15:21:49 +02:00
17 changed files with 419 additions and 169 deletions
+11 -1
View File
@@ -1,12 +1,22 @@
---
name: Bug report
about: Create a report to help me improve Excalidraw
about: When something is clearly broken. Everything else is a feature request.
title: 'BUG: '
labels: ''
assignees: ''
---
Help me help you. I am a one man show doing this plugin as a part time hobby. There is no point in flooding me with issues, if there are too many, and they are poorly documented, I will just ignore them. Sorry...
Before creating a bug report, please
1. review recent release notes - maybe there is already an answer,
2. search issues (including closed ones) to see if there is anything similar.
⚠️ I will have to close all recorded bugs that do not provide this background information. Sorry, I need to control my workload/time. ⚠️
--------
**Your environment**
Please run `Command Palette/Show Debug info` in Obsidian and paste the result here.
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "2.1.4",
"version": "2.1.6",
"minAppVersion": "1.1.6",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",
+1
View File
@@ -601,6 +601,7 @@ export class ExcalidrawAutomate {
"excalidraw-export-dark"?: boolean;
"excalidraw-export-padding"?: number;
"excalidraw-export-pngscale"?: number;
"excalidraw-export-embed-scene"?: boolean;
"excalidraw-default-mode"?: "view" | "zen";
"excalidraw-onload-script"?: string;
"excalidraw-linkbutton-opacity"?: number;
+101 -17
View File
@@ -51,6 +51,7 @@ import { BinaryFiles, DataURL, SceneData } from "@zsviczian/excalidraw/types/exc
import { EmbeddedFile, MimeType } from "./EmbeddedFileLoader";
import { ConfirmationPrompt } from "./dialogs/Prompt";
import { getMermaidImageElements, getMermaidText, shouldRenderMermaid } from "./utils/MermaidUtils";
import { add } from "@zsviczian/excalidraw/types/excalidraw/ga";
type SceneDataWithFiles = SceneData & { files: BinaryFiles };
@@ -240,7 +241,11 @@ const estimateMaxLineLen = (text: string, originalText: string): number => {
const wrap = (text: string, lineLen: number) =>
lineLen ? wrapTextAtCharLength(text, lineLen, false, 0) : text;
const RE_TEXTELEMENTS = new RegExp(`^(%%\n)?${MD_TEXTELEMENTS}(?:\n|$)`, "m");
//WITHSECTION refers to back of the card note (see this.inputEl.onkeyup in SelectCard.ts)
const RE_TEXTELEMENTS_WITHSECTION_OK = new RegExp(`^#\n%%\n${MD_TEXTELEMENTS}(?:\n|$)`, "m");
const RE_TEXTELEMENTS_WITHSECTION_NOTOK = new RegExp(`#\n%%\n${MD_TEXTELEMENTS}(?:\n|$)`, "m");
const RE_TEXTELEMENTS_NOSECTION_OK = new RegExp(`^(%%\n)?${MD_TEXTELEMENTS}(?:\n|$)`, "m");
//The issue is that when editing in markdown embeds the user can delete the last enter causing two sections
//to collide. This is particularly problematic when the user is editing the lest section before # Text Elements
@@ -251,19 +256,70 @@ const RE_TEXTELEMENTS_FALLBACK_2 = new RegExp(`(.*)${MD_TEXTELEMENTS}(?:\n|$)`,
const RE_DRAWING = new RegExp(`(%%\n)?${MD_DRAWING}\n`);
export const getExcalidrawMarkdownHeaderSection = (data:string, keys?:[string,string][]):string => {
let trimLocation = data.search(RE_TEXTELEMENTS);
/* Expected markdown structure:
bla bla bla
#
%%
# Text Elements
*/
let trimLocation = data.search(RE_TEXTELEMENTS_WITHSECTION_OK);
let shouldFixTrailingHashtag = false;
if(trimLocation > 0) {
trimLocation += 2;
}
/* Expected markdown structure:
bla bla bla#
%%
# Text Elements
*/
if(trimLocation === -1) {
trimLocation = data.search(RE_TEXTELEMENTS_WITHSECTION_NOTOK);
if(trimLocation > 0) {
shouldFixTrailingHashtag = true;
}
}
/* Expected markdown structure
a)
bla bla bla
%%
# Text Elements
b)
bla bla bla
# Text Elements
*/
if(trimLocation === -1) {
trimLocation = data.search(RE_TEXTELEMENTS_NOSECTION_OK);
}
/* Expected markdown structure:
bla bla bla%%
# Text Elements
*/
if(trimLocation === -1) {
const res = data.match(RE_TEXTELEMENTS_FALLBACK_1);
if(res && Boolean(res[1])) {
trimLocation = res.index + res[1].length;
}
}
/* Expected markdown structure:
bla bla bla# Text Elements
*/
if(trimLocation === -1) {
const res = data.match(RE_TEXTELEMENTS_FALLBACK_2);
if(res && Boolean(res[1])) {
trimLocation = res.index + res[1].length;
}
}
}
/* Expected markdown structure:
a)
bla bla bla
# Drawing
b)
bla bla bla
%%
# Drawing
*/
if (trimLocation === -1) {
trimLocation = data.search(RE_DRAWING);
}
@@ -273,12 +329,14 @@ export const getExcalidrawMarkdownHeaderSection = (data:string, keys?:[string,st
let header = updateFrontmatterInString(data.substring(0, trimLocation),keys);
//this should be removed at a later time. Left it here to remediate 1.4.9 mistake
const REG_IMG = /(^---[\w\W]*?---\n)(!\[\[.*?]]\n(%%\n)?)/m; //(%%\n)? because of 1.4.8-beta... to be backward compatible with anyone who installed that version
/*const REG_IMG = /(^---[\w\W]*?---\n)(!\[\[.*?]]\n(%%\n)?)/m; //(%%\n)? because of 1.4.8-beta... to be backward compatible with anyone who installed that version
if (header.match(REG_IMG)) {
header = header.replace(REG_IMG, "$1");
}
}*/
//end of remove
return header.endsWith("\n") ? header : (header + "\n");
return shouldFixTrailingHashtag
? header + "\n#\n"
: header.endsWith("\n") ? header : (header + "\n");
}
@@ -580,19 +638,36 @@ export class ExcalidrawData {
data = data.substring(0, sceneJSONandPOS.pos);
//The Markdown # Text Elements take priority over the JSON text elements. Assuming the scenario in which the link was updated due to filename changes
//The Markdown # Text Elements take priority over the JSON text elements. Assuming the scenario in which the
//link was updated due to filename changes
//The .excalidraw JSON is modified to reflect the MD in case of difference
//Read the text elements into the textElements Map
let position = data.search(RE_TEXTELEMENTS);
let position = data.search(RE_TEXTELEMENTS_NOSECTION_OK);
if (position === -1) {
//resillience in case back of the note was saved right on top of text elements
// # back of note section
// ....# Text Elements
// ....
// --------------
// instead of
// --------------
// # back of note section
// ....
// # Text Elements
position = data.search(RE_TEXTELEMENTS_FALLBACK_2);
}
if (position === -1) {
await this.setTextMode(textMode, false);
this.loaded = true;
return true; //Text Elements header does not exist
}
const textElementsMatch = data.match(new RegExp(`^((%%\n)?${MD_TEXTELEMENTS}(?:\n|$))`, "m"))[0]
position += textElementsMatch.length;
data = data.slice(position);
const normalMatch = data.match(new RegExp(`^((%%\n)?${MD_TEXTELEMENTS}(?:\n|$))`, "m"));
const textElementsMatch = normalMatch
? normalMatch[0]
: data.match(new RegExp(`(.*${MD_TEXTELEMENTS}(?:\n|$))`, "m"))[0];
data = data.slice(textElementsMatch.length);
this.textElementCommentedOut = textElementsMatch.startsWith("%%\n");
position = 0;
let parts;
@@ -1360,14 +1435,18 @@ export class ExcalidrawData {
const processedIds = new Set<string>();
fileIds.forEach((fileId,idx)=>{
if(processedIds.has(fileId)) {
const file = this.getFile(fileId);
const embeddedFile = this.getFile(fileId);
const equation = this.getEquation(fileId);
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.isLocalLink || (file.file && (file.file.extension !== "md" || this.plugin.isExcalidrawFile(file.file))))) {
if (embeddedFile &&
(embeddedFile.isHyperLink || embeddedFile.isLocalLink ||
(embeddedFile.file &&
(embeddedFile.file.extension !== "md" || this.plugin.isExcalidrawFile(embeddedFile.file))
)
)
) {
return;
}
if(mermaid) {
@@ -1379,6 +1458,11 @@ export class ExcalidrawData {
return;
}
if(!embeddedFile && !equation && !mermaid) {
//processing freshly pasted images from likely anotehr instance of excalidraw (e.g. Excalidraw.com, or another Obsidian instance)
return;
}
const newId = fileid();
(scene
.elements
@@ -1387,8 +1471,8 @@ export class ExcalidrawData {
.fileId = newId;
dirty = true;
processedIds.add(newId);
if(file) {
this.setFile(newId as FileId,new EmbeddedFile(this.plugin,this.file.path,file.linkParts.original));
if(embeddedFile) {
this.setFile(newId as FileId,new EmbeddedFile(this.plugin,this.file.path,embeddedFile.linkParts.original));
}
if(equation) {
this.setEquation(newId as FileId, {latex:equation.latex, isLoaded:false});
+208 -119
View File
@@ -9,14 +9,12 @@ import {
MarkdownView,
request,
requireApiVersion,
requestUrl,
} from "obsidian";
//import * as React from "react";
//import * as ReactDOM from "react-dom";
//import Excalidraw from "@zsviczian/excalidraw";
import {
ExcalidrawElement,
ExcalidrawGenericElement,
ExcalidrawImageElement,
ExcalidrawTextElement,
FileId,
@@ -82,12 +80,10 @@ import {
} from "./utils/FileUtils";
import {
checkExcalidrawVersion,
debug,
embedFontsInSVG,
errorlog,
getEmbeddedFilenameParts,
getExportTheme,
getLinkParts,
getPNG,
getPNGScale,
getSVG,
@@ -105,7 +101,7 @@ import {
shouldEmbedScene,
getContainerElement,
} from "./utils/Utils";
import { cleanSectionHeading, getLeaf, getParentOfClass, obsidianPDFQuoteWithRef, openLeaf } from "./utils/ObsidianUtils";
import { cleanBlockRef, cleanSectionHeading, getLeaf, getParentOfClass, obsidianPDFQuoteWithRef, openLeaf } from "./utils/ObsidianUtils";
import { splitFolderAndFilename } from "./utils/FileUtils";
import { ConfirmationPrompt, GenericInputPrompt, NewFileActions, Prompt, linkPrompt } from "./dialogs/Prompt";
import { ClipboardData } from "@zsviczian/excalidraw/types/excalidraw/clipboard";
@@ -140,7 +136,9 @@ import { CustomMutationObserver, isDebugMode } from "./utils/DebugHelper";
import { extractCodeBlocks, postOpenAI } from "./utils/AIUtils";
import { Mutable } from "@zsviczian/excalidraw/types/excalidraw/utility-types";
import { SelectCard } from "./dialogs/SelectCard";
import { link } from "fs";
const EMBEDDABLE_SEMAPHORE_TIMEOUT = 2000;
const PREVENT_RELOAD_TIMEOUT = 2000;
declare const PLUGIN_VERSION:string;
@@ -394,7 +392,7 @@ export default class ExcalidrawView extends TextFileView {
if (!this.excalidrawAPI || !this.file) {
return;
}
if (this.app.isMobile) {
if (DEVICE.isMobile) {
const prompt = new Prompt(
this.app,
"Please provide filename",
@@ -620,7 +618,7 @@ export default class ExcalidrawView extends TextFileView {
public setPreventReload() {
this.semaphores.preventReload = true;
const self = this;
this.preventReloadResetTimer = setTimeout(()=>self.semaphores.preventReload = false,2000);
this.preventReloadResetTimer = setTimeout(()=>self.semaphores.preventReload = false,PREVENT_RELOAD_TIMEOUT);
}
public clearPreventReloadTimer() {
@@ -647,7 +645,7 @@ export default class ExcalidrawView extends TextFileView {
public clearEmbeddableIsEditingSelf() {
const self = this;
this.clearEmbeddableIsEditingSelfTimer();
this.editingSelfResetTimer = setTimeout(()=>self.semaphores.embeddableIsEditingSelf = false,2000);
this.editingSelfResetTimer = setTimeout(()=>self.semaphores.embeddableIsEditingSelf = false,EMBEDDABLE_SEMAPHORE_TIMEOUT);
}
async save(preventReload: boolean = true, forcesave: boolean = false) {
@@ -681,30 +679,28 @@ export default class ExcalidrawView extends TextFileView {
}
try {
const allowSave = Boolean (
(this.semaphores.dirty !== null && this.semaphores.dirty) ||
this.semaphores.autosaving ||
forcesave
); //dirty == false when view.file == null;
const scene = this.getScene();
if (this.compatibilityMode) {
await this.excalidrawData.syncElements(scene);
} else if (
await this.excalidrawData.syncElements(scene, this.excalidrawAPI.getAppState().selectedElementIds)
&& !this.semaphores.popoutUnload //Obsidian going black after REACT 18 migration when closing last leaf on popout
) {
await this.loadDrawing(
false,
this.excalidrawAPI.getSceneElementsIncludingDeleted().filter((el:ExcalidrawElement)=>el.isDeleted)
);
}
const allowSave = this.isDirty() || forcesave; //removed this.semaphores.autosaving
if(isDebugMode) console.log({allowSave, isDirty: this.isDirty(), autosaving: this.semaphores.autosaving, forcesave});
if (allowSave) {
const scene = this.getScene();
if (this.compatibilityMode) {
await this.excalidrawData.syncElements(scene);
} else if (
await this.excalidrawData.syncElements(scene, this.excalidrawAPI.getAppState().selectedElementIds)
&& !this.semaphores.popoutUnload //Obsidian going black after REACT 18 migration when closing last leaf on popout
) {
await this.loadDrawing(
false,
this.excalidrawAPI.getSceneElementsIncludingDeleted().filter((el:ExcalidrawElement)=>el.isDeleted)
);
}
//reload() is triggered indirectly when saving by the modifyEventHandler in main.ts
//prevent reload is set here to override reload when not wanted: typically when the user is editing
//and we do not want to interrupt the flow by reloading the drawing into the canvas.
this.clearDirty();
this.clearPreventReloadTimer();
this.semaphores.preventReload = preventReload;
@@ -717,7 +713,7 @@ export default class ExcalidrawView extends TextFileView {
triggerReload = (this.lastSaveTimestamp === this.file.stat.mtime) &&
!preventReload && forcesave;
this.lastSaveTimestamp = this.file.stat.mtime;
this.clearDirty();
//this.clearDirty(); //moved to right after allow save, to avoid autosave collision with load drawing
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/629
//there were odd cases when preventReload semaphore did not get cleared and consequently a synchronized image
@@ -942,6 +938,46 @@ export default class ExcalidrawView extends TextFileView {
return false;
}
private getLinkTextForElement(
selectedText:SelectedElementWithLink,
selectedElementWithLink?:SelectedElementWithLink
): {
linkText: string,
selectedElement: ExcalidrawElement,
} {
if (selectedText?.id || selectedElementWithLink?.id) {
const selectedTextElement: ExcalidrawTextElement = selectedText.id
? this.excalidrawAPI.getSceneElements().find((el:ExcalidrawElement)=>el.id === selectedText.id)
: null;
const selectedElement = selectedElementWithLink.id
? this.excalidrawAPI.getSceneElements().find((el:ExcalidrawElement)=>el.id === selectedElementWithLink.id)
: null;
let linkText =
selectedElementWithLink?.text ??
(this.textMode === TextMode.parsed
? this.excalidrawData.getRawText(selectedText.id)
: selectedText.text);
const partsArray = REGEX_LINK.getResList(linkText);
if (!linkText || partsArray.length === 0) {
//the container link takes precedence over the text link
if(selectedTextElement?.containerId) {
const container = getContainerElement(selectedTextElement, {elements: this.excalidrawAPI.getSceneElements()});
if(container) {
linkText = container.link;
}
}
if(!linkText || partsArray.length === 0) {
linkText = selectedTextElement?.link;
}
}
return {linkText, selectedElement: selectedTextElement ?? selectedElement};
}
return {linkText: null, selectedElement: null};
}
async linkClick(
ev: MouseEvent | null,
selectedText: SelectedElementWithLink,
@@ -959,41 +995,16 @@ export default class ExcalidrawView extends TextFileView {
let file = null;
let subpath: string = null;
let linkText: string = null;
if (selectedText?.id || selectedElementWithLink?.id) {
const selectedTextElement: ExcalidrawTextElement = selectedText.id
? this.excalidrawAPI.getSceneElements().find((el:ExcalidrawElement)=>el.id === selectedText.id)
: null;
linkText =
selectedElementWithLink?.text ??
(this.textMode === TextMode.parsed
? this.excalidrawData.getRawText(selectedText.id)
: selectedText.text);
const partsArray = REGEX_LINK.getResList(linkText);
if (!linkText || partsArray.length === 0) {
//the container link takes precedence over the text link
if(selectedTextElement?.containerId) {
const container = getContainerElement(selectedTextElement, {elements: this.excalidrawAPI.getSceneElements()});
if(container) {
linkText = container.link;
}
}
if(!linkText) {
linkText = selectedTextElement?.link;
}
}
let {linkText, selectedElement} = this.getLinkTextForElement(selectedText, selectedElementWithLink);
//if (selectedText?.id || selectedElementWithLink?.id) {
if (selectedElement) {
if (!linkText) {
return;
}
linkText = linkText.replaceAll("\n", ""); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/187
if(this.handleLinkHookCall(selectedTextElement,linkText,ev)) return;
if(this.handleLinkHookCall(selectedElement,linkText,ev)) return;
if(openExternalLink(linkText, this.app)) return;
const result = await linkPrompt(linkText, this.app, this);
@@ -1010,8 +1021,6 @@ export default class ExcalidrawView extends TextFileView {
selectedImage.fileId,
).latex;
GenericInputPrompt.Prompt(this,this.plugin,this.app,t("ENTER_LATEX"),undefined,equation, undefined, 3).then(async (formula: string) => {
// const prompt = new Prompt(this.app, t("ENTER_LATEX"), equation, "");
// prompt.openAndGetValue(async (formula: string) => {
if (!formula || formula === equation) {
return;
}
@@ -1182,7 +1191,7 @@ export default class ExcalidrawView extends TextFileView {
? null
: this.getSelectedImageElement();
const selectedElementWithLink =
selectedImage?.id || selectedText?.id
(selectedImage?.id || selectedText?.id)
? null
: this.getSelectedElementWithLink();
this.linkClick(
@@ -1247,8 +1256,8 @@ export default class ExcalidrawView extends TextFileView {
onload() {
const apiMissing = Boolean(typeof this.containerEl.onWindowMigrated === "undefined")
//@ts-ignore
if(!app.isMobile && !apiMissing) this.containerEl.onWindowMigrated(()=>this.leaf.rebuildView());
const doc = app.isMobile?document:this.containerEl.ownerDocument;
if(!DEVICE.isMobile && !apiMissing) this.containerEl.onWindowMigrated(()=>this.leaf.rebuildView());
const doc = DEVICE.isMobile?document:this.containerEl.ownerDocument;
this.ownerDocument = doc;
this.ownerWindow = this.ownerDocument.defaultView;
this.plugin.getPackage(this.ownerWindow);
@@ -1336,7 +1345,7 @@ export default class ExcalidrawView extends TextFileView {
const self = this;
this.slidingPanesListner = () => {
if (self.excalidrawAPI) {
self.refresh();
self.refreshCanvasOffset();
}
};
let rootSplit = this.app.workspace.rootSplit as WorkspaceItem as WorkspaceItemExt;
@@ -1380,7 +1389,7 @@ export default class ExcalidrawView extends TextFileView {
const { offsetLeft, offsetTop } = target;
if (offsetLeft !== self.offsetLeft || offsetTop != self.offsetTop) {
if (self.excalidrawAPI) {
self.refresh();
self.refreshCanvasOffset();
}
self.offsetLeft = offsetLeft;
self.offsetTop = offsetTop;
@@ -1474,19 +1483,19 @@ export default class ExcalidrawView extends TextFileView {
return;
}
const st = api.getAppState();
const editing = st.editingElement !== null;
const isEditing = st.editingElement !== null;
const isDragging = st.draggingElement !== null;
//this will reset positioning of the cursor in case due to the popup keyboard,
//or the command palette, or some other unexpected reason the onResize would not fire...
this.refresh();
this.refreshCanvasOffset();
if (
this.semaphores.dirty &&
this.semaphores.dirty == this.file?.path &&
this.isDirty() &&
this.plugin.settings.autosave &&
!this.semaphores.forceSaving &&
!this.semaphores.autosaving &&
!this.semaphores.embeddableIsEditingSelf &&
!editing &&
st.draggingElement === null //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/630
!isEditing &&
!isDragging //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/630
) {
//console.log("autosave");
this.autosaveTimer = null;
@@ -1513,14 +1522,22 @@ export default class ExcalidrawView extends TextFileView {
};
this.autosaveFunction = timer;
this.resetAutosaveTimer();
}
private resetAutosaveTimer() {
if(!this.autosaveFunction) return;
if (this.autosaveTimer) {
clearTimeout(this.autosaveTimer);
this.autosaveTimer = null;
} // clear previous timer if one exists
this.autosaveTimer = setTimeout(
timer,
this.autosaveFunction,
this.plugin.settings.autosaveInterval,
);
}
//save current drawing when user closes workspace leaf
@@ -1683,10 +1700,26 @@ export default class ExcalidrawView extends TextFileView {
) await sleep(50); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/734
}
const filenameParts = getEmbeddedFilenameParts(state.subpath);
const filenameParts = getEmbeddedFilenameParts(
(state.subpath && state.subpath.startsWith("#^group") && !state.subpath.startsWith("#^group="))
? "#^group=" + state.subpath.substring(7)
: (state.subpath && state.subpath.startsWith("#^area") && !state.subpath.startsWith("#^area="))
? "#^area=" + state.subpath.substring(6)
: state.subpath
);
if(filenameParts.hasBlockref) {
setTimeout(async () => {
await waitForExcalidraw();
if(filenameParts.blockref && !filenameParts.hasGroupref) {
if(!self.getScene()?.elements.find(el=>el.id === filenameParts.blockref)) {
const cleanQuery = cleanSectionHeading(filenameParts.blockref).replaceAll(" ","");
const blocks = await self.getBackOfTheNoteBlocks();
if(blocks.includes(cleanQuery)) {
this.setMarkdownView(state);
return;
}
}
}
setTimeout(()=>self.zoomToElementId(filenameParts.blockref, filenameParts.hasGroupref));
});
}
@@ -1731,13 +1764,20 @@ export default class ExcalidrawView extends TextFileView {
}
}
self.selectElementsMatchingQuery(
if(!self.selectElementsMatchingQuery(
elements,
query,
!api.getAppState().viewModeEnabled,
filenameParts.hasSectionref,
filenameParts.hasGroupref
);
)) {
const cleanQuery = cleanSectionHeading(query[0]).replaceAll(" ","");
const sections = await self.getBackOfTheNoteSections();
if(sections.includes(cleanQuery)) {
self.setMarkdownView(state);
return;
}
}
});
}
@@ -2121,7 +2161,7 @@ export default class ExcalidrawView extends TextFileView {
this.semaphores.preventReload = false;
const penEnabled =
this.plugin.settings.defaultPenMode === "always" ||
(this.plugin.settings.defaultPenMode === "mobile" && app.isMobile);
(this.plugin.settings.defaultPenMode === "mobile" && DEVICE.isMobile);
const api = this.excalidrawAPI;
if (api) {
//isLoaded flags that a new file is being loaded, isLoaded will be true after loadDrawing completes
@@ -2223,13 +2263,14 @@ export default class ExcalidrawView extends TextFileView {
}
public setDirty(debug?:number) {
if(this.semaphores.saving) return; //do not set dirty if saving
if(isDebugMode) console.log(debug);
this.semaphores.dirty = this.file?.path;
this.diskIcon.querySelector("svg").addClass("excalidraw-dirty");
if(!this.semaphores.viewunload && this.toolsPanelRef?.current) {
this.toolsPanelRef.current.setDirty(true);
}
if(!this.app.isMobile) {
if(!DEVICE.isMobile) {
if(requireApiVersion("0.16.0")) {
//@ts-ignore
this.leaf.tabHeaderInnerTitleEl.style.color="var(--color-accent)"
@@ -2237,6 +2278,10 @@ export default class ExcalidrawView extends TextFileView {
}
}
public isDirty() {
return Boolean(this.semaphores.dirty) && (this.semaphores.dirty === this.file?.path);
}
public clearDirty() {
if(this.semaphores.viewunload) return;
const api = this.excalidrawAPI;
@@ -2252,7 +2297,7 @@ export default class ExcalidrawView extends TextFileView {
this.previousSceneVersion = this.getSceneVersion(el);
}
this.diskIcon.querySelector("svg").removeClass("excalidraw-dirty");
if(!app.isMobile) {
if(!DEVICE.isMobile) {
if(requireApiVersion("0.16.0")) {
//@ts-ignore
this.leaf.tabHeaderInnerTitleEl.style.color=""
@@ -2302,17 +2347,17 @@ export default class ExcalidrawView extends TextFileView {
return ICON_NAME;
}
setMarkdownView() {
setMarkdownView(eState?: any) {
this.plugin.excalidrawFileModes[this.id || this.file.path] = "markdown";
this.plugin.setMarkdownView(this.leaf);
this.plugin.setMarkdownView(this.leaf, eState);
}
public async openAsMarkdown() {
public async openAsMarkdown(eState?: any) {
if (this.plugin.settings.compress === true) {
this.excalidrawData.disableCompression = true;
await this.save(true, true);
}
this.setMarkdownView();
this.setMarkdownView(eState);
}
public async convertExcalidrawToMD() {
@@ -2891,6 +2936,7 @@ export default class ExcalidrawView extends TextFileView {
currentStrokeOptions: st.currentStrokeOptions,
previousGridSize: st.previousGridSize,
frameRendering: st.frameRendering,
objectsSnapModeEnabled: st.objectsSnapModeEnabled,
},
prevTextMode: this.prevTextMode,
files,
@@ -2901,7 +2947,7 @@ export default class ExcalidrawView extends TextFileView {
* ExcalidrawAPI refreshes canvas offsets
* @returns
*/
private refresh() {
private refreshCanvasOffset() {
if(this.contentEl.clientWidth === 0 || this.contentEl.clientHeight === 0) return;
const api = this.excalidrawAPI;
if (!api) {
@@ -3002,41 +3048,52 @@ export default class ExcalidrawView extends TextFileView {
if (!linktext) {
if(!this.currentPosition) return;
linktext = "";
const selectedElement = getTextElementAtPointer(this.currentPosition, this);
if (!selectedElement || !selectedElement.text) {
const selectedEl = getTextElementAtPointer(this.currentPosition, this);
if (!selectedEl || !selectedEl.text) {
const selectedImgElement =
getImageElementAtPointer(this.currentPosition, this);
const selectedElementWithLink = (selectedImgElement?.id || selectedImgElement?.id)
? null
: getElementWithLinkAtPointer(this.currentPosition, this);
element = this.excalidrawAPI.getSceneElements().find((el:ExcalidrawElement)=>el.id === selectedImgElement.id);
if (!selectedImgElement || !selectedImgElement.fileId) {
if ((!selectedImgElement || !selectedImgElement.fileId) && !selectedElementWithLink?.id) {
return;
}
if (!this.excalidrawData.hasFile(selectedImgElement.fileId)) {
return;
if (selectedImgElement?.id) {
if (!this.excalidrawData.hasFile(selectedImgElement.fileId)) {
return;
}
const ef = this.excalidrawData.getFile(selectedImgElement.fileId);
if (
(ef.isHyperLink || ef.isLocalLink) || //web images don't have a preview
(IMAGE_TYPES.contains(ef.file.extension)) || //images don't have a preview
(ef.file.extension.toLowerCase() === "pdf") || //pdfs don't have a preview
(this.plugin.ea.isExcalidrawFile(ef.file))
) {//excalidraw files don't have a preview
linktext = getLinkTextFromLink(element.link);
if(!linktext) return;
} else {
const ref = ef.linkParts.ref
? `#${ef.linkParts.isBlockRef ? "^" : ""}${ef.linkParts.ref}`
: "";
linktext =
ef.file.path + ref;
}
}
const ef = this.excalidrawData.getFile(selectedImgElement.fileId);
if (
(ef.isHyperLink || ef.isLocalLink) || //web images don't have a preview
(IMAGE_TYPES.contains(ef.file.extension)) || //images don't have a preview
(ef.file.extension.toLowerCase() === "pdf") || //pdfs don't have a preview
(this.plugin.ea.isExcalidrawFile(ef.file))
) {//excalidraw files don't have a preview
linktext = getLinkTextFromLink(element.link);
if (selectedElementWithLink?.id) {
linktext = getLinkTextFromLink(selectedElementWithLink.text);
if(!linktext) return;
} else {
const ref = ef.linkParts.ref
? `#${ef.linkParts.isBlockRef ? "^" : ""}${ef.linkParts.ref}`
: "";
linktext =
ef.file.path + ref;
}
} else {
element = this.excalidrawAPI.getSceneElements().filter((el:ExcalidrawElement)=>el.id === selectedElement.id)[0];
const text: string =
this.textMode === TextMode.parsed
? this.excalidrawData.getRawText(selectedElement.id)
: selectedElement.text;
const {linkText, selectedElement} = this.getLinkTextForElement(selectedEl, selectedEl);
element = selectedElement;
/*this.excalidrawAPI.getSceneElements().filter((el:ExcalidrawElement)=>el.id === selectedElement.id)[0];
const text: string =
this.textMode === TextMode.parsed
? this.excalidrawData.getRawText(selectedElement.id)
: selectedElement.text;*/
linktext = getLinkTextFromLink(text);
linktext = getLinkTextFromLink(linkText);
if(!linktext) return;
}
}
@@ -3381,7 +3438,7 @@ export default class ExcalidrawView extends TextFileView {
}
if (data.elements) {
const self = this;
setTimeout(() => self.save(false), 300);
setTimeout(() => self.save(), 300); //removed prevent reload = false, as reload was triggered when pasted containers were processed and there was a conflict with the new elements
}
return true;
}
@@ -4052,6 +4109,19 @@ export default class ExcalidrawView extends TextFileView {
}
}
private async getBackOfTheNoteSections() {
return (await this.app.metadataCache.blockCache.getForFile({ isCancelled: () => false },this.file))
.blocks.filter((b: any) => b.display && b.node?.type === "heading")
.filter((b: any) => !MD_EX_SECTIONS.includes(b.display))
.map((b: any) => cleanSectionHeading(b.display));
}
private async getBackOfTheNoteBlocks() {
return (await this.app.metadataCache.blockCache.getForFile({ isCancelled: () => false },this.file))
.blocks.filter((b:any) => b.display && b.node && b.node.hasOwnProperty("type") && b.node.hasOwnProperty("id"))
.map((b:any) => cleanBlockRef(b.node.id));
}
public getSingleSelectedImage(): {imageEl: ExcalidrawImageElement, embeddedFile: EmbeddedFile} {
if(!this.excalidrawAPI) return null;
const els = this.getViewSelectedElements().filter(el=>el.type==="image");
@@ -4064,13 +4134,9 @@ export default class ExcalidrawView extends TextFileView {
}
public async insertBackOfTheNoteCard() {
const sections = (await this.app.metadataCache.blockCache
.getForFile({ isCancelled: () => false },this.file))
.blocks.filter((b: any) => b.display && b.node?.type === "heading")
.filter((b: any) => !MD_EX_SECTIONS.includes(b.display))
.map((b: any) => cleanSectionHeading(b.display));
const sections = await this.getBackOfTheNoteSections();
const selectCardDialog = new SelectCard(this.app,this,sections);
selectCardDialog.start();
selectCardDialog.start();
}
public async convertImageElWithURLToLocalFile(data: {imageEl: ExcalidrawImageElement, embeddedFile: EmbeddedFile}) {
@@ -4176,6 +4242,7 @@ export default class ExcalidrawView extends TextFileView {
const onContextMenu = (elements: readonly ExcalidrawElement[], appState: AppState, onClose: (callback?: () => void) => void) => {
const contextMenuActions = [];
const api = this.excalidrawAPI as ExcalidrawImperativeAPI;
const areElementsSelected = Object.keys(api.getAppState().selectedElementIds).length>0
if(this.isLinkSelected()) {
contextMenuActions.push([
@@ -4290,6 +4357,17 @@ export default class ExcalidrawView extends TextFileView {
]);
}
if(areElementsSelected) {
contextMenuActions.push([
renderContextMenuAction(
t("COPY_ELEMENT_LINK"),
() => {
this.copyLinkToSelectedElementToClipboard("");
},
onClose
),
]);
}
contextMenuActions.push([
renderContextMenuAction(
t("INSERT_CARD"),
@@ -4670,7 +4748,7 @@ export default class ExcalidrawView extends TextFileView {
this.toolsPanelRef.current.updatePosition();
}
if(this.ownerDocument !== document) {
this.refresh(); //because resizeobserver in Excalidraw does not seem to work when in Obsidian Window
this.refreshCanvasOffset(); //because resizeobserver in Excalidraw does not seem to work when in Obsidian Window
}
} catch (err) {
errorlog({
@@ -4836,7 +4914,7 @@ export default class ExcalidrawView extends TextFileView {
}
const maxZoom = this.plugin.settings.zoomToFitMaxLevel;
const elements = api.getSceneElements().filter((el:ExcalidrawElement)=>el.width<10000 && el.height<10000);
if((app.isMobile && elements.length>1000) || elements.length>2500) {
if((DEVICE.isMobile && elements.length>1000) || elements.length>2500) {
if(justLoaded) api.scrollToContent();
return;
}
@@ -4909,13 +4987,23 @@ export default class ExcalidrawView extends TextFileView {
this.plugin.saveSettings();
}
/**
*
* @param elements
* @param query
* @param selectResult
* @param exactMatch
* @param selectGroup
* @returns true if element found, false if no element is found.
*/
public selectElementsMatchingQuery(
elements: ExcalidrawElement[],
query: string[],
selectResult: boolean = true,
exactMatch: boolean = false, //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530
selectGroup: boolean = false,
) {
):boolean {
let match = getTextElementsMatchingQuery(
elements.filter((el: ExcalidrawElement) => el.type === "text"),
query,
@@ -4928,7 +5016,7 @@ export default class ExcalidrawView extends TextFileView {
if (match.length === 0) {
new Notice("I could not find a matching text element");
return;
return false;
}
if(selectGroup) {
@@ -4939,6 +5027,7 @@ export default class ExcalidrawView extends TextFileView {
}
this.zoomToElements(selectResult,match);
return true;
}
public zoomToElements(
+39 -1
View File
@@ -17,6 +17,44 @@ 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>
`,
"2.1.6":`
## Two minor fixes
- Scaling of LaTeX formulas when the formula is changed
- If the back of the note card only contains a block embed ${String.fromCharCode(96)}![[embed]]${String.fromCharCode(96)} this got removed when saving the Excalidraw file. This issue has been present since November, 2021 (v1.4.9).
`,
"2.1.5":`
## New
- Save "Snap to objects" with the scene state. If this is the only change you make to the scene, force save it using CTRL+S (note, use CTRL on Mac as well).
- Added "Copy markdown link" to the context menu.
## Fixed
- Paste operation occasionally duplicated text elements.
- Pasting multiple instances of the same image from excalidraw.com or another instance of Obsidian, or pasting an image from anywhere and making copies with ALT/OPT + drag immediately after pasting (before autosave triggered) led to broken images when reopening the drawing.
- CTRL/CMD+Click on a Text Element with an element link did not work (previously, you had to click the top right link indicator). Now, you can click anywhere on the element.
- Hover preview for elements with a link only worked when hovering over the element link. Now, you can hover anywhere. If there are multiple elements with links, the top-level element will take precedence.
- Link navigation within drawing when the "Focus on Existing Tab" feature is enabled under "Links, transclusion and TODOs" in settings works again.
- If a link points to a back-of-the-card section or block the drawing will automatically switch to markdown view mode and navigate to the block or section.
- DynamicSytle, dark mode when canvas background is set to transparent.
- Scale to maintain the aspect ratio of a markdown notes embedded as images.
- You can now borrow interactive markdown embeds to tables, blockquotes, list elements and callouts - not just paragraphs.
- Back of the drawing cards:
- Leaving the Section Name empty when creating the first back of the card note resulted in an error.
- If you add the markdown comment (${String.fromCharCode(96)}%%${String.fromCharCode(96)}) directly before ${String.fromCharCode(96)}# Text Elements${String.fromCharCode(96)}, a trailing ${String.fromCharCode(96)}#${String.fromCharCode(96)} will be added to your document, when adding a back of the card note. This is to hide the markdown comment from the card. The trailing (empty) ${String.fromCharCode(96)}#${String.fromCharCode(96)} will not be visible in reading mode, pdf exports, and when publishing with Obsidian Publish.
Here's a sample markdown structure of your document:
${String.fromCharCode(96,96,96)}markdown
---
excalidraw-plugin: parsed
---
# Your back of the card section
bla bla bla
#
%%
# Text Elements
... the rest of the Excalidraw file
${String.fromCharCode(96,96,96)}
`,
"2.1.4":`
## Fixed
- Fixed the **aspect ratio** of an Excalidraw embedded within another Excalidraw **not updating**. [#1707](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1707)
@@ -33,7 +71,7 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
- **Enhanced annotation and cropping** of images in Markdown documents:
- Newly embedded **links will now follow the style of the original link**. If the original format was a ${String.fromCharCode(96)}![markdown](link)${String.fromCharCode(96)}, the annotated file will follow this format. For ${String.fromCharCode(96)}[[wiki links]]${String.fromCharCode(96)}, it will follow that style. Additionally, if an alias was specified like ${String.fromCharCode(96)}[[link|alias]]${String.fromCharCode(96)}, the annotated or cropped image will retain the alias.
- Introduced a new setting under "Saving" titled **"Preserve image size when annotating"**. This setting is disabled by default. When enabled, the embed link replacing the annotated image will maintain the size of the original image.
- Option to **automaticaly embed the scene in exported PNG and SVG image files**. Including the scene will allow users to open the picture on Excalidraw.com or in another Obsidian Vault as an editable Excalidraw file.New setting is under the Export category. The new frontmatter tag is: ${String.fromCharCode(96)}excalidraw-export-embed-scene: true/false${String.fromCharCode(96)}.
- Option to **automatically embed the scene in exported PNG and SVG image files**. Including the scene will allow users to open the picture on Excalidraw.com or in another Obsidian Vault as an editable Excalidraw file.New setting is under the Export category. The new frontmatter tag is: ${String.fromCharCode(96)}excalidraw-export-embed-scene: true/false${String.fromCharCode(96)}.
`,
"2.1.3":`
This is a republish of 2.1.2 with a minor change. Sorry about the frequent releases. I will hold back for a few weeks now.
+8 -3
View File
@@ -5,7 +5,6 @@ import { getEA } from "src";
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
import { getExcalidrawMarkdownHeaderSection } from "src/ExcalidrawData";
import { MD_EX_SECTIONS } from "src/constants/constants";
import { ExcalidrawImageElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
import { cleanSectionHeading } from "src/utils/ObsidianUtils";
@@ -29,7 +28,7 @@ export class SelectCard extends FuzzySuggestModal<string> {
if (e.key == "Enter") {
if (this.containerEl.innerText.includes(t("EMPTY_SECTION_MESSAGE"))) {
const item = this.inputEl.value;
if(MD_EX_SECTIONS.includes(item)) {
if(item === "" || MD_EX_SECTIONS.includes(item)) {
new Notice(t("INVALID_SECTION_NAME"));
this.close();
return;
@@ -37,7 +36,13 @@ export class SelectCard extends FuzzySuggestModal<string> {
(async () => {
const data = view.data;
const header = getExcalidrawMarkdownHeaderSection(data);
view.data = data.replace(header, header + `\n# ${item}\n\n`);
const body = data.split(header)[1];
const shouldAddHashtag = body && body.startsWith("%%");
const shouldRemoveTrailingHashtag = header.endsWith("#\n");
view.data = data.replace(
header,
(shouldRemoveTrailingHashtag ? header.substring(0,header.length-2) : header) +
`\n# ${item}\n\n${shouldAddHashtag ? "#\n" : ""}`);
await view.forceSave(true);
let watchdog = 0;
await sleep(200);
+7
View File
@@ -197,6 +197,7 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
' "excalidraw-export-dark"?: boolean;\n' +
' "excalidraw-export-padding"?: number;\n' +
' "excalidraw-export-pngscale"?: number;\n' +
' "excalidraw-export-embed-scene"?: boolean;\n' +
' "excalidraw-default-mode"?: "view" | "zen";\n' +
' "excalidraw-onload-script"?: string;\n' +
' "excalidraw-linkbutton-opacity"?: number;\n' +
@@ -821,6 +822,12 @@ export const FRONTMATTER_KEYS_INFO: SuggesterInfo[] = [
desc: "If this key is present it will override the default excalidraw embed and export setting. This only affects export to PNG. Specify the export scale for the image. The typical range is between 0.5 and 5, but you can experiment with other values as well.",
after: ": 1",
},
{
field: "excalidraw-export-embed-scene",
code: null,
desc: "If this key is present it will override the default excalidraw embed and export setting.",
after: ": false",
},
{
field: "open-md",
code: null,
+2 -1
View File
@@ -48,6 +48,7 @@ export default {
NEW_IN_POPOUT_WINDOW_EMBED: "Create new drawing - IN A POPOUT WINDOW - and embed into active document",
TOGGLE_LOCK: "Toggle Text Element between edit RAW and PREVIEW",
DELETE_FILE: "Delete selected image or Markdown file from Obsidian Vault",
COPY_ELEMENT_LINK: "Copy markdown link for selected element(s)",
INSERT_LINK_TO_ELEMENT:
`Copy markdown link for selected element to clipboard. ${labelCTRL()}+CLICK to copy 'group=' link. ${labelSHIFT()}+CLICK to copy an 'area=' link. ${labelALT()}+CLICK to watch a help video.`,
INSERT_LINK_TO_ELEMENT_GROUP:
@@ -684,7 +685,7 @@ FILENAME_HEAD: "Filename",
SELECT_SECTION_OR_TYPE_NEW:
"Select existing section or type name of a new section then press Enter.",
INVALID_SECTION_NAME: "Invalid section name.",
EMPTY_SECTION_MESSAGE: "Hit enter to create a new Section",
EMPTY_SECTION_MESSAGE: "Type the Section Name and hit enter to create a new Section",
//EmbeddedFileLoader.ts
INFINITE_LOOP_WARNING:
+12 -11
View File
@@ -42,7 +42,8 @@ import {
LOCALE,
IMAGE_TYPES,
MD_TEXTELEMENTS,
setExcalidrawPlugin
setExcalidrawPlugin,
DEVICE
} from "./constants/constants";
import {
VIRGIL_FONT,
@@ -381,7 +382,7 @@ export default class ExcalidrawPlugin extends Plugin {
private getOpenObsidianDocuments(): Document[] {
const visitedDocs = new Set<Document>();
this.app.workspace.iterateAllLeaves((leaf)=>{
const ownerDocument = this.app.isMobile?document:leaf.view.containerEl.ownerDocument;
const ownerDocument = DEVICE.isMobile?document:leaf.view.containerEl.ownerDocument;
if(!ownerDocument) return;
if(visitedDocs.has(ownerDocument)) return;
visitedDocs.add(ownerDocument);
@@ -1087,7 +1088,7 @@ export default class ExcalidrawPlugin extends Plugin {
name: t("NEW_IN_POPOUT_WINDOW"),
checkCallback: (checking: boolean) => {
if (checking) {
return !app.isMobile
return !DEVICE.isMobile;
}
this.createAndOpenDrawing(getDrawingFilename(this.settings), "popout-window");
},
@@ -1159,7 +1160,7 @@ export default class ExcalidrawPlugin extends Plugin {
name: t("NEW_IN_POPOUT_WINDOW_EMBED"),
checkCallback: (checking: boolean) => {
if (checking) {
return !app.isMobile && Boolean(this.app.workspace.getActiveViewOfType(MarkdownView));
return !DEVICE.isMobile && Boolean(this.app.workspace.getActiveViewOfType(MarkdownView));
}
insertDrawingToDoc("popout-window");
return true;
@@ -2580,14 +2581,14 @@ export default class ExcalidrawPlugin extends Plugin {
//!Temporary hack
//https://discord.com/channels/686053708261228577/817515900349448202/1031101635784613968
if (this.app.isMobile && newActiveviewEV && !previouslyActiveEV) {
if (DEVICE.isMobile && newActiveviewEV && !previouslyActiveEV) {
const navbar = document.querySelector("body>.app-container>.mobile-navbar");
if(navbar && navbar instanceof HTMLDivElement) {
navbar.style.position="relative";
}
}
if (this.app.isMobile && !newActiveviewEV && previouslyActiveEV) {
if (DEVICE.isMobile && !newActiveviewEV && previouslyActiveEV) {
const navbar = document.querySelector("body>.app-container>.mobile-navbar");
if(navbar && navbar instanceof HTMLDivElement) {
navbar.style.position="";
@@ -2991,7 +2992,7 @@ export default class ExcalidrawPlugin extends Plugin {
}
if(opts.applyLefthandedMode) setLeftHandedMode(this.settings.isLeftHanded);
if(opts.reEnableAutosave) this.settings.autosave = true;
this.settings.autosaveInterval = app.isMobile
this.settings.autosaveInterval = DEVICE.isMobile
? this.settings.autosaveIntervalMobile
: this.settings.autosaveIntervalDesktop;
}
@@ -3018,7 +3019,7 @@ export default class ExcalidrawPlugin extends Plugin {
public triggerEmbedUpdates(filepath?: string) {
const visitedDocs = new Set<Document>();
app.workspace.iterateAllLeaves((leaf)=>{
const ownerDocument = app.isMobile?document:leaf.view.containerEl.ownerDocument;
const ownerDocument = DEVICE.isMobile?document:leaf.view.containerEl.ownerDocument;
if(!ownerDocument) return;
if(visitedDocs.has(ownerDocument)) return;
visitedDocs.add(ownerDocument);
@@ -3193,7 +3194,7 @@ export default class ExcalidrawPlugin extends Plugin {
return file.path;
}
public async setMarkdownView(leaf: WorkspaceLeaf) {
public async setMarkdownView(leaf: WorkspaceLeaf, eState?: any) {
const state = leaf.view.getState();
//Note v2.0.19: I have absolutely no idea why I thought this is necessary. Removing this.
@@ -3209,7 +3210,7 @@ export default class ExcalidrawPlugin extends Plugin {
state,
popstate: true,
} as ViewState,
{ focus: true },
eState ? eState : { focus: true },
);
}
@@ -3231,7 +3232,7 @@ export default class ExcalidrawPlugin extends Plugin {
}
public async exportLibrary() {
if (this.app.isMobile) {
if (DEVICE.isMobile) {
const prompt = new Prompt(
this.app,
"Please provide a filename",
+3 -1
View File
@@ -172,7 +172,9 @@ export class EmbeddableMenu {
view.updateScene({appState: {activeEmbeddable: null}});
const paragraphs = (await app.metadataCache.blockCache
.getForFile({ isCancelled: () => false },file))
.blocks.filter((b: any) => b.display && b.node?.type === "paragraph");
.blocks.filter((b: any) => b.display && b.node &&
(b.node.type === "paragraph" || b.node.type === "blockquote" || b.node.type === "listItem" || b.node.type === "table" || b.node.type === "callout")
);
const values = ["entire-file"].concat(paragraphs);
const display = [t("SHOW_ENTIRE_FILE")].concat(
paragraphs.map((b: any) => `${b.node?.id ? `#^${b.node.id}: ` : ``}${b.display.trim()}`));
+3 -3
View File
@@ -8,7 +8,7 @@ import {
TextComponent,
TFile,
} from "obsidian";
import { GITHUB_RELEASES, VIEW_TYPE_EXCALIDRAW } from "./constants/constants";
import { DEVICE, GITHUB_RELEASES, VIEW_TYPE_EXCALIDRAW } from "./constants/constants";
import ExcalidrawView from "./ExcalidrawView";
import { t } from "./lang/helpers";
import type ExcalidrawPlugin from "./main";
@@ -674,7 +674,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.autosaveIntervalDesktop.toString())
.onChange(async (value) => {
this.plugin.settings.autosaveIntervalDesktop = parseInt(value);
this.plugin.settings.autosaveInterval = app.isMobile
this.plugin.settings.autosaveInterval = DEVICE.isMobile
? this.plugin.settings.autosaveIntervalMobile
: this.plugin.settings.autosaveIntervalDesktop;
this.applySettingsUpdate();
@@ -693,7 +693,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.setValue(this.plugin.settings.autosaveIntervalMobile.toString())
.onChange(async (value) => {
this.plugin.settings.autosaveIntervalMobile = parseInt(value);
this.plugin.settings.autosaveInterval = app.isMobile
this.plugin.settings.autosaveInterval = DEVICE.isMobile
? this.plugin.settings.autosaveIntervalMobile
: this.plugin.settings.autosaveIntervalDesktop;
this.applySettingsUpdate();
+2 -6
View File
@@ -97,16 +97,12 @@ export class CropImage {
withTheme: false,
isMask: false,
}
const isRotated = this.imageEA.getElements().some(el=>el.type === "image" && el.angle !== 0);
const images = Object.values(this.imageEA.imagesDict);
if(images.length === 1) {
if(!isRotated && (images.length === 1)) {
return images[0].dataURL;
}
return await this.imageEA.createPNGBase64(null,1,exportSettings,null,null,0);
const imageSVG = await this.imageEA.createSVG(null,false,exportSettings,null,null,0);
const svgData = new XMLSerializer().serializeToString(imageSVG);
return `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svgData)))}`;
// const blob = new Blob([svgString], { type: 'image/svg+xml' });
// return `data:image/svg+xml;base64,${await blobToBase64(blob)}`;
}
private async buildSVG(): Promise<SVGSVGElement> {
+4
View File
@@ -32,6 +32,10 @@ export const setDynamicStyle = (
view?.excalidrawAPI?.getAppState?.()?.theme === "light" ||
view?.excalidrawData?.scene?.appState?.theme === "light";
if (color==="transparent") {
color = "#ffffff";
}
const darker = "#101010";
const lighter = "#f0f0f0";
const step = 10;
+1 -1
View File
@@ -22,7 +22,7 @@ export const getElementsAtPointer = (
y <= pointer.y &&
y + h >= pointer.y
);
});
}).reverse();
};
export const getTextElementAtPointer = (pointer: any, view: ExcalidrawView) => {
+7 -1
View File
@@ -285,7 +285,13 @@ export const openLeaf = ({
leaf = l;
}
});
if(leaf) return {leaf, promise: Promise.resolve()};
if(leaf) {
if(openState) {
const promise = leaf.openFile(file, openState);
return {leaf, promise};
}
return {leaf, promise: Promise.resolve()};
}
}
leaf = fnGetLeaf();
const promise = leaf.openFile(file, openState);
+9 -3
View File
@@ -19,6 +19,8 @@ import {
IMAGE_TYPES,
FRONTMATTER_KEYS,
EXCALIDRAW_PLUGIN,
getCommonBoundingBox,
DEVICE,
} from "../constants/constants";
import ExcalidrawPlugin from "../main";
import { ExcalidrawElement } from "@zsviczian/excalidraw/types/excalidraw/element/types";
@@ -157,6 +159,10 @@ const rotate = (
export const rotatedDimensions = (
element: ExcalidrawElement,
): [number, number, number, number] => {
const bb = getCommonBoundingBox([element]);
return [bb.minX, bb.minY, bb.maxX - bb.minX, bb.maxY - bb.minY];
//removed with 2.1.5... will delete later
if (element.angle === 0) {
return [element.x, element.y, element.width, element.height];
}
@@ -447,10 +453,10 @@ export const scaleLoadedImage = (
for (const f of files.filter((f:any)=>{
if(!Boolean(EXCALIDRAW_PLUGIN)) return true; //this should never happen
const ef = EXCALIDRAW_PLUGIN.filesMaster.get(f.id);
if(!ef) return false;
if(!ef) return true; //mermaid SVG or equation
const file = EXCALIDRAW_PLUGIN.app.vault.getAbstractFileByPath(ef.path.replace(/#.*$/,"").replace(/\|.*$/,""));
if(!file || (file instanceof TFolder)) return false;
return EXCALIDRAW_PLUGIN.isExcalidrawFile(file as TFile)
return (file as TFile).extension==="md" || EXCALIDRAW_PLUGIN.isExcalidrawFile(file as TFile)
})) {
const [w_image, h_image] = [f.size.width, f.size.height];
const imageAspectRatio = f.size.width / f.size.height;
@@ -503,7 +509,7 @@ export const setDocLeftHandedMode = (isLeftHanded: boolean, ownerDocument:Docume
export const setLeftHandedMode = (isLeftHanded: boolean) => {
const visitedDocs = new Set<Document>();
app.workspace.iterateAllLeaves((leaf) => {
const ownerDocument = app.isMobile?document:leaf.view.containerEl.ownerDocument;
const ownerDocument = DEVICE.isMobile?document:leaf.view.containerEl.ownerDocument;
if(!ownerDocument) return;
if(visitedDocs.has(ownerDocument)) return;
visitedDocs.add(ownerDocument);