mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
1.4.13
This commit is contained in:
@@ -92,7 +92,7 @@ export class EmbeddedFile {
|
||||
if (!this.linkParts.height) {
|
||||
this.linkParts.height = this.plugin.settings.mdSVGmaxHeight;
|
||||
}
|
||||
this.file = this.plugin.app.metadataCache.getFirstLinkpathDest(
|
||||
this.file = app.metadataCache.getFirstLinkpathDest(
|
||||
this.linkParts.path,
|
||||
hostPath,
|
||||
);
|
||||
@@ -108,7 +108,7 @@ export class EmbeddedFile {
|
||||
|
||||
private fileChanged(): boolean {
|
||||
if (!this.file) {
|
||||
this.file = this.plugin.app.metadataCache.getFirstLinkpathDest(
|
||||
this.file = app.metadataCache.getFirstLinkpathDest(
|
||||
this.linkParts.path,
|
||||
this.hostPath,
|
||||
); // maybe the file has synchronized in the mean time
|
||||
@@ -149,7 +149,7 @@ export class EmbeddedFile {
|
||||
|
||||
public isLoaded(isDark: boolean): boolean {
|
||||
if (!this.file) {
|
||||
this.file = this.plugin.app.metadataCache.getFirstLinkpathDest(
|
||||
this.file = app.metadataCache.getFirstLinkpathDest(
|
||||
this.linkParts.path,
|
||||
this.hostPath,
|
||||
); // maybe the file has synchronized in the mean time
|
||||
@@ -218,7 +218,6 @@ export class EmbeddedFilesLoader {
|
||||
};
|
||||
|
||||
let hasSVGwithBitmap = false;
|
||||
const app = this.plugin.app;
|
||||
const isExcalidrawFile = this.plugin.isExcalidrawFile(file);
|
||||
if (
|
||||
!(
|
||||
|
||||
@@ -1141,7 +1141,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
*/
|
||||
setView(view: ExcalidrawView | "first" | "active"): ExcalidrawView {
|
||||
if (view == "active") {
|
||||
const v = this.plugin.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
const v = app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (!(v instanceof ExcalidrawView)) {
|
||||
return;
|
||||
}
|
||||
@@ -1149,7 +1149,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
}
|
||||
if (view == "first") {
|
||||
const leaves =
|
||||
this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
if (!leaves || leaves.length == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -1283,7 +1283,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
* @returns
|
||||
*/
|
||||
viewToggleFullScreen(forceViewMode: boolean = false): void {
|
||||
if (this.plugin.app.isMobile) {
|
||||
if (app.isMobile) {
|
||||
errorMessage("mobile not supported", "viewToggleFullScreen()");
|
||||
return;
|
||||
}
|
||||
@@ -1618,7 +1618,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface {
|
||||
return null;
|
||||
}
|
||||
const leaf = getNewOrAdjacentLeaf(this.plugin, this.targetView.leaf);
|
||||
leaf.openFile(file, {active: false});
|
||||
leaf.openFile(file, {active: true});
|
||||
return leaf;
|
||||
};
|
||||
|
||||
@@ -2008,7 +2008,9 @@ async function getTemplate(
|
||||
|
||||
let groupElements:ExcalidrawElement[] = scene.elements;
|
||||
if(filenameParts.hasGroupref) {
|
||||
const el = scene.elements.filter((el: ExcalidrawElement) => el.id === filenameParts.blockref);
|
||||
const el = filenameParts.hasSectionref
|
||||
? getTextElementsMatchingQuery(scene.elements,["# "+filenameParts.sectionref],true)
|
||||
: scene.elements.filter((el: ExcalidrawElement)=>el.id===filenameParts.blockref);
|
||||
if(el.length > 0) {
|
||||
groupElements = plugin.ea.getElementsInTheSameGroupWithElement(el[0],scene.elements)
|
||||
}
|
||||
@@ -2094,6 +2096,7 @@ export async function createSVG(
|
||||
: null;
|
||||
let elements = template?.elements ?? [];
|
||||
elements = elements.concat(automateElements);
|
||||
padding = padding ?? plugin.settings.exportPaddingSVG;
|
||||
const svg = await getSVG(
|
||||
{
|
||||
//createAndOpenDrawing
|
||||
@@ -2113,8 +2116,29 @@ export async function createSVG(
|
||||
exportSettings?.withBackground ?? plugin.settings.exportWithBackground,
|
||||
withTheme: exportSettings?.withTheme ?? plugin.settings.exportWithTheme,
|
||||
},
|
||||
padding ?? plugin.settings.exportPaddingSVG,
|
||||
padding,
|
||||
);
|
||||
const filenameParts = getEmbeddedFilenameParts(templatePath);
|
||||
if(
|
||||
!filenameParts.hasGroupref &&
|
||||
(filenameParts.hasBlockref || filenameParts.hasSectionref)
|
||||
) {
|
||||
let el = filenameParts.hasSectionref
|
||||
? getTextElementsMatchingQuery(elements,["# "+filenameParts.sectionref],true)
|
||||
: elements.filter((el: ExcalidrawElement)=>el.id===filenameParts.blockref);
|
||||
if(el.length>0) {
|
||||
const containerId = el[0].containerId;
|
||||
if(containerId) {
|
||||
el = el.concat(elements.filter((el: ExcalidrawElement)=>el.id === containerId));
|
||||
}
|
||||
const elBB = plugin.ea.getBoundingBox(el);
|
||||
const drawingBB = plugin.ea.getBoundingBox(elements);
|
||||
svg.viewBox.baseVal.x = elBB.topX - drawingBB.topX;
|
||||
svg.viewBox.baseVal.y = elBB.topY - drawingBB.topY;
|
||||
svg.viewBox.baseVal.width = elBB.width + 2*padding;
|
||||
svg.viewBox.baseVal.height = elBB.height + 2*padding;
|
||||
}
|
||||
}
|
||||
if (template?.hasSVGwithBitmap) {
|
||||
svg.setAttribute("hasbitmap", "true");
|
||||
}
|
||||
@@ -2242,3 +2266,35 @@ export const search = async (view: ExcalidrawView) => {
|
||||
|
||||
ea.targetView.selectElementsMatchingQuery(elements, query);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param elements
|
||||
* @param query
|
||||
* @param exactMatch - when searching for section header exactMatch should be set to true
|
||||
* @returns the elements matching the query
|
||||
*/
|
||||
export const getTextElementsMatchingQuery = (
|
||||
elements: ExcalidrawElement[],
|
||||
query: string[],
|
||||
exactMatch: boolean = false, //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530
|
||||
): ExcalidrawElement[] => {
|
||||
if (!elements || elements.length === 0 || !query || query.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return elements.filter((el: any) =>
|
||||
el.type === "text" &&
|
||||
query.some((q) => {
|
||||
if (exactMatch) {
|
||||
const text = el.rawText.toLowerCase().split("\n")[0].trim();
|
||||
const m = text.match(/^#*(# .*)/);
|
||||
if (!m || m.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
return m[1] === q.toLowerCase();
|
||||
}
|
||||
const text = el.rawText.toLowerCase().replaceAll("\n", " ").trim();
|
||||
return text.match(q.toLowerCase()); //to distinguish between "# frame" and "# frame 1" https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
MarkdownView,
|
||||
request,
|
||||
Platform,
|
||||
requireApiVersion,
|
||||
} from "obsidian";
|
||||
//import * as React from "react";
|
||||
//import * as ReactDOM from "react-dom";
|
||||
@@ -42,7 +43,7 @@ import {
|
||||
LOCAL_PROTOCOL,
|
||||
} from "./Constants";
|
||||
import ExcalidrawPlugin from "./main";
|
||||
import { repositionElementsToCursor, ExcalidrawAutomate } from "./ExcalidrawAutomate";
|
||||
import { repositionElementsToCursor, ExcalidrawAutomate, getTextElementsMatchingQuery } from "./ExcalidrawAutomate";
|
||||
import { t } from "./lang/helpers";
|
||||
import {
|
||||
ExcalidrawData,
|
||||
@@ -57,10 +58,12 @@ import {
|
||||
getNewUniqueFilepath,
|
||||
} from "./utils/FileUtils";
|
||||
import {
|
||||
awaitNextAnimationFrame,
|
||||
checkExcalidrawVersion,
|
||||
debug,
|
||||
embedFontsInSVG,
|
||||
errorlog,
|
||||
getEmbeddedFilenameParts,
|
||||
getExportTheme,
|
||||
getLinkParts,
|
||||
getPNG,
|
||||
@@ -69,6 +72,7 @@ import {
|
||||
getSVGPadding,
|
||||
getWithBackground,
|
||||
hasExportTheme,
|
||||
isVersionNewerThanOther,
|
||||
scaleLoadedImage,
|
||||
svgToBase64,
|
||||
viewportCoordsToSceneCoords,
|
||||
@@ -89,6 +93,8 @@ import { ToolsPanel } from "./menu/ToolsPanel";
|
||||
import { ScriptEngine } from "./Scripts";
|
||||
import { getTextElementAtPointer, getImageElementAtPointer, getElementWithLinkAtPointer } from "./utils/GetElementAtPointer";
|
||||
import { execArgv } from "process";
|
||||
import { findLastIndex } from "@zsviczian/excalidraw/types/utils";
|
||||
import { fileOpen } from "@zsviczian/excalidraw/types/data/filesystem";
|
||||
|
||||
|
||||
export enum TextMode {
|
||||
@@ -131,7 +137,7 @@ export const addFiles = async (
|
||||
}
|
||||
if (s.dirty) {
|
||||
//debug({where:"ExcalidrawView.addFiles",file:view.file.name,dataTheme:view.excalidrawData.scene.appState.theme,before:"updateScene",state:scene.appState})
|
||||
view.updateScene({
|
||||
await view.updateScene({
|
||||
elements: s.scene.elements,
|
||||
appState: s.scene.appState,
|
||||
commitToHistory: false,
|
||||
@@ -315,7 +321,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
filename = `${filename}.excalidraw`;
|
||||
const folderpath = splitFolderAndFilename(this.file.path).folderpath;
|
||||
await checkAndCreateFolder(app.vault, folderpath); //create folder if it does not exist
|
||||
await checkAndCreateFolder(folderpath); //create folder if it does not exist
|
||||
const fname = getNewUniqueFilepath(
|
||||
app.vault,
|
||||
filename,
|
||||
@@ -1024,6 +1030,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
});
|
||||
|
||||
this.setupAutosaveTimer();
|
||||
super.onload();
|
||||
}
|
||||
|
||||
//this is to solve sliding panes bug
|
||||
@@ -1278,7 +1285,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.clearDirty();
|
||||
}
|
||||
|
||||
async zoomToElementId(id: string) {
|
||||
async zoomToElementId(id: string, hasGroupref:boolean) {
|
||||
let counter = 0;
|
||||
while (!this.excalidrawAPI && counter++<100) await sleep(50); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/734
|
||||
const api = this.excalidrawAPI;
|
||||
@@ -1286,17 +1293,14 @@ export default class ExcalidrawView extends TextFileView {
|
||||
return;
|
||||
}
|
||||
const sceneElements = api.getSceneElements();
|
||||
|
||||
let elements = sceneElements.filter((el: ExcalidrawElement) => el.id === id);
|
||||
if(elements.length === 0) {
|
||||
if(!id.startsWith("group_")) return;
|
||||
id = id.split("group_")[1];
|
||||
elements = sceneElements.filter((el: ExcalidrawElement) => el.id === id);
|
||||
if(elements.length === 0) return;
|
||||
if(elements.length === 0) return;
|
||||
if(hasGroupref) {
|
||||
const groupElements = this.plugin.ea.getElementsInTheSameGroupWithElement(elements[0],sceneElements)
|
||||
if(groupElements.length>0) {
|
||||
elements = groupElements;
|
||||
}
|
||||
if(elements.length === 0) return;
|
||||
}
|
||||
|
||||
this.preventAutozoom();
|
||||
@@ -1325,16 +1329,13 @@ export default class ExcalidrawView extends TextFileView {
|
||||
];
|
||||
}
|
||||
|
||||
if (state.subpath && state.subpath.length > 2) {
|
||||
if (state.subpath[1] === "^") {
|
||||
const id = state.subpath.substring(2);
|
||||
setTimeout(() => self.zoomToElementId(id), 300);
|
||||
} else {
|
||||
query = [`# ${state.subpath.substring(1)}`];
|
||||
}
|
||||
const filenameParts = getEmbeddedFilenameParts(state.subpath);
|
||||
if(filenameParts.hasBlockref) {
|
||||
setTimeout(()=>self.zoomToElementId(filenameParts.blockref, filenameParts.hasGroupref),300);
|
||||
}
|
||||
|
||||
if (state.line && state.line > 0) {
|
||||
if(filenameParts.hasSectionref) {
|
||||
query = [`# ${filenameParts.sectionref}`]
|
||||
} else if (state.line && state.line > 0) {
|
||||
query = [this.data.split("\n")[state.line - 1]];
|
||||
}
|
||||
|
||||
@@ -1347,14 +1348,14 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if (!api) {
|
||||
return;
|
||||
}
|
||||
const elements = api
|
||||
.getSceneElements()
|
||||
.filter((el: ExcalidrawElement) => el.type === "text");
|
||||
const elements = api.getSceneElements();
|
||||
|
||||
self.selectElementsMatchingQuery(
|
||||
elements,
|
||||
query,
|
||||
!api.getAppState().viewModeEnabled,
|
||||
state.subpath && state.subpath.length>2 && state.subpath[1]!=="^",
|
||||
filenameParts.hasSectionref,
|
||||
filenameParts.hasGroupref
|
||||
);
|
||||
}, 300);
|
||||
}
|
||||
@@ -1615,7 +1616,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if(this.getSceneVersion(inData.scene.elements) !== this.previousSceneVersion) {
|
||||
this.setDirty(3);
|
||||
}
|
||||
this.excalidrawAPI.updateScene({elements: sceneElements});
|
||||
await this.updateScene({elements: sceneElements});
|
||||
if(reloadFiles) this.loadSceneFiles();
|
||||
} catch(e) {
|
||||
errorlog({
|
||||
@@ -1655,7 +1656,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
//debug({where:"ExcalidrawView.loadDrawing",file:this.file.name,dataTheme:excalidrawData.appState.theme,before:"updateScene"})
|
||||
api.setLocalFont(this.plugin.settings.experimentalEnableFourthFont);
|
||||
|
||||
this.updateScene(
|
||||
await this.updateScene(
|
||||
{
|
||||
elements: excalidrawData.elements.concat(deletedElements??[]), //need to preserve deleted elements during autosave if images, links, etc. are updated
|
||||
appState: {
|
||||
@@ -1727,6 +1728,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
//console.log(debug);
|
||||
this.semaphores.dirty = this.file?.path;
|
||||
this.diskIcon.querySelector("svg").addClass("excalidraw-dirty");
|
||||
if(!app.isMobile) {
|
||||
if(requireApiVersion("0.16.0")) {
|
||||
//@ts-ignore
|
||||
this.leaf.tabHeaderInnerTitleEl.style.color="var(--color-accent)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public clearDirty() {
|
||||
@@ -1740,6 +1747,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.previousSceneVersion = this.getSceneVersion(el);
|
||||
}
|
||||
this.diskIcon.querySelector("svg").removeClass("excalidraw-dirty");
|
||||
if(!app.isMobile) {
|
||||
if(requireApiVersion("0.16.0")) {
|
||||
//@ts-ignore
|
||||
this.leaf.tabHeaderInnerTitleEl.style.color=""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public initializeToolsIconPanelAfterLoading() {
|
||||
@@ -1801,6 +1814,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
this.plugin.openDrawing(
|
||||
await this.plugin.convertSingleExcalidrawToMD(this.file),
|
||||
"active-pane",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2270,12 +2284,13 @@ export default class ExcalidrawView extends TextFileView {
|
||||
const elements = newElementsOnTop
|
||||
? el.concat(newElements.filter((e) => !removeList.includes(e.id)))
|
||||
: newElements.filter((e) => !removeList.includes(e.id)).concat(el);
|
||||
this.updateScene({
|
||||
|
||||
await this.updateScene({
|
||||
elements,
|
||||
commitToHistory: true,
|
||||
});
|
||||
|
||||
if (images) {
|
||||
if (images && images !== {}) {
|
||||
const files: BinaryFileData[] = [];
|
||||
Object.keys(images).forEach((k) => {
|
||||
files.push({
|
||||
@@ -3140,7 +3155,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
: this.leaf;
|
||||
await leaf.openFile(
|
||||
file,
|
||||
subpath ? { active: false, eState: { subpath } } : {active:false},
|
||||
subpath ? { active: false, eState: { subpath } } : {active:false}, //active false: to avoid taking the focus from ExcaliBrain
|
||||
); //if file exists open file and jump to reference
|
||||
//app.workspace.setActiveLeaf(leaf, true, true); //0.15.4 ExcaliBrain focus issue
|
||||
} catch (e) {
|
||||
@@ -3300,30 +3315,26 @@ export default class ExcalidrawView extends TextFileView {
|
||||
query: string[],
|
||||
selectResult: boolean = true,
|
||||
exactMatch: boolean = false, //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530
|
||||
selectGroup: boolean = false,
|
||||
) {
|
||||
if (!elements || elements.length === 0 || !query || query.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const match = elements.filter((el: any) =>
|
||||
query.some((q) => {
|
||||
if (exactMatch) {
|
||||
const text = el.rawText.toLowerCase().split("\n")[0].trim();
|
||||
const m = text.match(/^#*(# .*)/);
|
||||
if (!m || m.length !== 2) {
|
||||
return false;
|
||||
}
|
||||
return m[1] === q.toLowerCase();
|
||||
}
|
||||
const text = el.rawText.toLowerCase().replaceAll("\n", " ").trim();
|
||||
return text.match(q.toLowerCase()); //to distinguish between "# frame" and "# frame 1" https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/530
|
||||
}),
|
||||
let match = getTextElementsMatchingQuery(
|
||||
elements.filter((el: ExcalidrawElement) => el.type === "text"),
|
||||
query,
|
||||
exactMatch
|
||||
);
|
||||
|
||||
if (match.length === 0) {
|
||||
new Notice("I could not find a matching text element");
|
||||
return;
|
||||
}
|
||||
|
||||
if(selectGroup) {
|
||||
const groupElements = this.plugin.ea.getElementsInTheSameGroupWithElement(match[0],elements)
|
||||
if(groupElements.length>0) {
|
||||
match = groupElements;
|
||||
}
|
||||
}
|
||||
|
||||
this.zoomToElements(selectResult,match);
|
||||
}
|
||||
|
||||
@@ -3388,7 +3399,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
.filter((el: ExcalidrawElement) => elementIDs.contains(el.id));
|
||||
}
|
||||
|
||||
public async copyLinkToSelectedElementToClipboard() {
|
||||
public async copyLinkToSelectedElementToClipboard(groupLink:boolean) {
|
||||
const elements = this.getViewSelectedElements();
|
||||
if (elements.length < 1) {
|
||||
new Notice(t("INSERT_LINK_TO_ELEMENT_ERROR"));
|
||||
@@ -3405,12 +3416,12 @@ export default class ExcalidrawView extends TextFileView {
|
||||
"",
|
||||
);
|
||||
navigator.clipboard.writeText(
|
||||
`[[${this.file.path}#^${elementId}${alias ? `|${alias}` : ``}]]`,
|
||||
`[[${this.file.path}#^${groupLink?"group=":""}${elementId}${alias ? `|${alias}` : ``}]]`,
|
||||
);
|
||||
new Notice(t("INSERT_LINK_TO_ELEMENT_READY"));
|
||||
}
|
||||
|
||||
public updateScene(
|
||||
public async updateScene(
|
||||
scene: {
|
||||
elements?: ExcalidrawElement[];
|
||||
appState?: any;
|
||||
@@ -3428,6 +3439,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
scene.elements = api.restore(scene).elements;
|
||||
}
|
||||
try {
|
||||
await awaitNextAnimationFrame(); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/747
|
||||
api.updateScene(scene);
|
||||
} catch (e) {
|
||||
errorlog({
|
||||
@@ -3440,6 +3452,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if (!shouldRestoreElements) {
|
||||
//second attempt
|
||||
try {
|
||||
await awaitNextAnimationFrame(); //https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/747
|
||||
scene.elements = api.restore(scene).elements;
|
||||
api.updateScene(scene);
|
||||
} catch (e) {
|
||||
@@ -3455,6 +3468,7 @@ export default class ExcalidrawView extends TextFileView {
|
||||
warningUnknowSeriousError();
|
||||
}
|
||||
}
|
||||
await awaitNextAnimationFrame()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
} from "./utils/Utils";
|
||||
import { isObsidianThemeDark } from "./utils/ObsidianUtils";
|
||||
import { splitFolderAndFilename } from "./utils/FileUtils";
|
||||
import * as internal from "stream";
|
||||
|
||||
interface imgElementAttributes {
|
||||
file?: TFile;
|
||||
@@ -120,10 +121,18 @@ const getIMG = async (
|
||||
scale = 5;
|
||||
}
|
||||
|
||||
//In case of PNG I cannot change the viewBox to select the area of the element
|
||||
//being referenced. For PNG only the group reference works
|
||||
const quickPNG = !filenameParts.hasGroupref
|
||||
? await getQuickImagePreview(plugin, file.path, "png")
|
||||
: undefined;
|
||||
|
||||
const png =
|
||||
(await getQuickImagePreview(plugin, file.path, "png")) ??
|
||||
quickPNG ??
|
||||
(await createPNG(
|
||||
file.path,
|
||||
filenameParts.hasGroupref
|
||||
? filenameParts.filepath + filenameParts.linkpartReference
|
||||
: file.path,
|
||||
scale,
|
||||
exportSettings,
|
||||
loader,
|
||||
@@ -140,14 +149,17 @@ const getIMG = async (
|
||||
img.src = URL.createObjectURL(png);
|
||||
return img;
|
||||
}
|
||||
const quickSVG = await getQuickImagePreview(plugin, file.path, "svg");
|
||||
if (quickSVG) {
|
||||
img.setAttribute("src", svgToBase64(quickSVG));
|
||||
return img;
|
||||
|
||||
if(!(filenameParts.hasBlockref || filenameParts.hasSectionref)) {
|
||||
const quickSVG = await getQuickImagePreview(plugin, file.path, "svg");
|
||||
if (quickSVG) {
|
||||
img.setAttribute("src", svgToBase64(quickSVG));
|
||||
return img;
|
||||
}
|
||||
}
|
||||
const svgSnapshot = (
|
||||
await createSVG(
|
||||
filenameParts.hasGroupref
|
||||
filenameParts.hasGroupref || filenameParts.hasBlockref || filenameParts.hasSectionref
|
||||
? filenameParts.filepath + filenameParts.linkpartReference
|
||||
: file.path,
|
||||
true,
|
||||
@@ -185,7 +197,7 @@ const createImageDiv = async (
|
||||
const img = await getIMG(attr);
|
||||
return createDiv(attr.style, (el) => {
|
||||
el.append(img);
|
||||
el.setAttribute("src", attr.file.path);
|
||||
el.setAttribute("src", attr.fname);
|
||||
if (attr.fwidth) {
|
||||
el.setAttribute("w", attr.fwidth);
|
||||
}
|
||||
@@ -201,13 +213,17 @@ const createImageDiv = async (
|
||||
}
|
||||
const src = el.getAttribute("src");
|
||||
if (src) {
|
||||
const srcParts = src.match(/([^#]*)(.*)/);
|
||||
if(!srcParts) return;
|
||||
plugin.openDrawing(
|
||||
vault.getAbstractFileByPath(src) as TFile,
|
||||
vault.getAbstractFileByPath(srcParts[1]) as TFile,
|
||||
ev[CTRL_OR_CMD]
|
||||
? "new-pane"
|
||||
: (ev.metaKey && !app.isMobile)
|
||||
? "popout-window"
|
||||
: "active-pane",
|
||||
true,
|
||||
srcParts[2],
|
||||
);
|
||||
} //.ctrlKey||ev.metaKey);
|
||||
});
|
||||
@@ -225,51 +241,56 @@ const createImageDiv = async (
|
||||
});
|
||||
};
|
||||
|
||||
const processInternalEmbeds = async (
|
||||
const processReadingMode = async (
|
||||
embeddedItems: NodeListOf<Element> | [HTMLElement],
|
||||
ctx: MarkdownPostProcessorContext,
|
||||
) => {
|
||||
//if not, then we are processing a non-excalidraw file in reading mode
|
||||
//in that cases embedded files will be displayed in an .internal-embed container
|
||||
//We are processing a non-excalidraw file in reading mode
|
||||
//Embedded files will be displayed in an .internal-embed container
|
||||
|
||||
//Iterating all the containers in the file to check which one is an excalidraw drawing
|
||||
//This is a for loop instead of embeddedItems.forEach() because processInternalEmbed at the end
|
||||
//is awaited, otherwise excalidraw images would not display in the Kanban plugin
|
||||
for (const maybeDrawing of embeddedItems) {
|
||||
//check to see if the file in the src attribute exists
|
||||
const fname = maybeDrawing.getAttribute("src")?.split("#")[0];
|
||||
if(!fname) continue;
|
||||
|
||||
const file = metadataCache.getFirstLinkpathDest(fname, ctx.sourcePath);
|
||||
|
||||
//if the embeddedFile exits and it is an Excalidraw file
|
||||
//then lets replace the .internal-embed with the generated PNG or SVG image
|
||||
if (file && file instanceof TFile && plugin.isExcalidrawFile(file)) {
|
||||
maybeDrawing.parentElement.replaceChild(
|
||||
await processInternalEmbed(maybeDrawing,file),
|
||||
maybeDrawing
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const processInternalEmbed = async (internalEmbedEl: Element, file: TFile ):Promise<HTMLDivElement> => {
|
||||
const attr: imgElementAttributes = {
|
||||
fname: "",
|
||||
fheight: "",
|
||||
fwidth: "",
|
||||
style: "",
|
||||
};
|
||||
let alt: string;
|
||||
let file: TFile;
|
||||
|
||||
//Iterating through all the containers to check which one is an excalidraw drawing
|
||||
//This is a for loop instead of embeddedItems.forEach() because createImageDiv at the end
|
||||
//is awaited, otherwise excalidraw images would not display in the Kanban plugin
|
||||
for (const maybeDrawing of embeddedItems) {
|
||||
//check to see if the file in the src attribute exists
|
||||
attr.fname = maybeDrawing.getAttribute("src");
|
||||
const fname = attr.fname?.split("#")[0]??"";
|
||||
file = metadataCache.getFirstLinkpathDest(fname, ctx.sourcePath);
|
||||
|
||||
//if the embeddedFile exits and it is an Excalidraw file
|
||||
//then lets replace the .internal-embed with the generated PNG or SVG image
|
||||
if (file && file instanceof TFile && plugin.isExcalidrawFile(file)) {
|
||||
attr.fwidth = maybeDrawing.getAttribute("width")
|
||||
? maybeDrawing.getAttribute("width")
|
||||
: getDefaultWidth(plugin);
|
||||
attr.fheight = maybeDrawing.getAttribute("height");
|
||||
alt = maybeDrawing.getAttribute("alt");
|
||||
if (alt === attr.fname) {
|
||||
alt = "";
|
||||
} //when the filename starts with numbers followed by a space Obsidian recognizes the filename as alt-text
|
||||
attr.style = "excalidraw-svg";
|
||||
processAltText(fname,alt,attr);
|
||||
const fnameParts = getEmbeddedFilenameParts(attr.fname);
|
||||
attr.fname = file?.path + (fnameParts.hasBlockref?fnameParts.linkpartReference:"");
|
||||
attr.file = file;
|
||||
const div = await createImageDiv(attr);
|
||||
maybeDrawing.parentElement.replaceChild(div, maybeDrawing);
|
||||
}
|
||||
}
|
||||
};
|
||||
const src = internalEmbedEl.getAttribute("src");
|
||||
if(!src) return;
|
||||
attr.fwidth = internalEmbedEl.getAttribute("width")
|
||||
? internalEmbedEl.getAttribute("width")
|
||||
: getDefaultWidth(plugin);
|
||||
attr.fheight = internalEmbedEl.getAttribute("height");
|
||||
let alt = internalEmbedEl.getAttribute("alt");
|
||||
attr.style = "excalidraw-svg";
|
||||
processAltText(src.split("#")[0],alt,attr);
|
||||
const fnameParts = getEmbeddedFilenameParts(src);
|
||||
attr.fname = file?.path + (fnameParts.hasBlockref||fnameParts.hasSectionref?fnameParts.linkpartReference:"");
|
||||
attr.file = file;
|
||||
return await createImageDiv(attr);
|
||||
}
|
||||
|
||||
const processAltText = (
|
||||
fname: string,
|
||||
@@ -282,7 +303,14 @@ const processAltText = (
|
||||
attr.fwidth = parts[2] ?? attr.fwidth;
|
||||
attr.fheight = parts[3] ?? attr.fheight;
|
||||
if (parts[4] && !parts[4].startsWith(fname)) {
|
||||
attr.style = `excalidraw-svg${parts[4] ? `-${parts[4]}` : ""}`;
|
||||
attr.style = `excalidraw-svg${`-${parts[4]}`}`;
|
||||
}
|
||||
if (
|
||||
(!parts[4] || parts[4]==="") &&
|
||||
(!parts[2] || parts[2]==="") &&
|
||||
parts[0] && parts[0] !== ""
|
||||
) {
|
||||
attr.style = `excalidraw-svg${`-${parts[0]}`}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,44 +328,9 @@ const tmpObsidianWYSIWYG = async (
|
||||
return;
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
const dataHeading = el.firstChild?.getAttribute("data-heading");
|
||||
const blockrefToUnrecognizedTarget = dataHeading?.startsWith("Unable to find section")
|
||||
|
||||
if (!blockrefToUnrecognizedTarget && !el.querySelector(".frontmatter")) {
|
||||
el.style.display = "none";
|
||||
return;
|
||||
}
|
||||
|
||||
let blockref = "";
|
||||
if(blockrefToUnrecognizedTarget) {
|
||||
const reg = new RegExp(`Unable to find section (.*) in ${file.basename}`);
|
||||
const m = dataHeading.match(/Unable to find section (.*) in 1test/);
|
||||
if(m && m.length>0) {
|
||||
blockref = m[1];
|
||||
}
|
||||
}
|
||||
|
||||
const attr: imgElementAttributes = {
|
||||
fname: ctx.sourcePath+blockref,
|
||||
fheight: "",
|
||||
fwidth: getDefaultWidth(plugin),
|
||||
style: "excalidraw-svg",
|
||||
};
|
||||
|
||||
attr.file = file;
|
||||
|
||||
el.empty();
|
||||
|
||||
if (!plugin.settings.experimentalLivePreview) {
|
||||
el.appendChild(await createImageDiv(attr));
|
||||
return;
|
||||
}
|
||||
|
||||
const div = createDiv();
|
||||
el.appendChild(div);
|
||||
|
||||
//The timeout gives time for obsidian to attach el to the displayed document
|
||||
//The timeout gives time for Obsidian to attach el to the displayed document
|
||||
//Once the element is attached, I can traverse up the dom tree to find .internal-embed
|
||||
//If internal embed is not found, it means the that the excalidraw.md file
|
||||
//is being rendered in "reading" mode. In that case, the image with the default width
|
||||
@@ -350,8 +343,7 @@ const tmpObsidianWYSIWYG = async (
|
||||
while(!el.parentElement && counter++<=50) await sleep(50);
|
||||
if(!el.parentElement) return;
|
||||
|
||||
|
||||
let internalEmbedDiv: HTMLElement = div;
|
||||
let internalEmbedDiv: HTMLElement = el;
|
||||
while (
|
||||
!internalEmbedDiv.hasClass("internal-embed") &&
|
||||
internalEmbedDiv.parentElement
|
||||
@@ -359,57 +351,39 @@ const tmpObsidianWYSIWYG = async (
|
||||
internalEmbedDiv = internalEmbedDiv.parentElement;
|
||||
}
|
||||
|
||||
const attr: imgElementAttributes = {
|
||||
fname: ctx.sourcePath,
|
||||
fheight: "",
|
||||
fwidth: getDefaultWidth(plugin),
|
||||
style: "excalidraw-svg",
|
||||
};
|
||||
|
||||
attr.file = file;
|
||||
|
||||
if (!internalEmbedDiv.hasClass("internal-embed")) {
|
||||
//We are processing the markdown preview of an actual Excalidraw file
|
||||
//This could be in a hover preview of the file
|
||||
//Or the file could be in markdown mode and the user switched markdown
|
||||
//view of the drawing to reading mode
|
||||
const mdPreviewSection = el.parentElement;
|
||||
if(!mdPreviewSection.hasClass("markdown-preview-section")) return;
|
||||
if(mdPreviewSection.hasAttribute("ready")) {
|
||||
mdPreviewSection.removeChild(el);
|
||||
return;
|
||||
}
|
||||
mdPreviewSection.setAttribute("ready","");
|
||||
el.empty();
|
||||
el.appendChild(await createImageDiv(attr));
|
||||
const imgDiv = await createImageDiv(attr);
|
||||
el.appendChild(imgDiv);
|
||||
return;
|
||||
}
|
||||
|
||||
if(internalEmbedDiv.hasAttribute("ready")) return;
|
||||
internalEmbedDiv.setAttribute("ready","");
|
||||
|
||||
internalEmbedDiv.empty();
|
||||
|
||||
const basename = splitFolderAndFilename(attr.fname).basename;
|
||||
const setAttr = () => {
|
||||
const hasWidth = internalEmbedDiv.getAttribute("width") && (internalEmbedDiv.getAttribute("width") !== "");
|
||||
const hasHeight = internalEmbedDiv.getAttribute("height") && (internalEmbedDiv.getAttribute("height") !== "");
|
||||
if (hasWidth) {
|
||||
attr.fwidth = internalEmbedDiv.getAttribute("width");
|
||||
}
|
||||
if (hasHeight) {
|
||||
attr.fheight = internalEmbedDiv.getAttribute("height");
|
||||
}
|
||||
|
||||
if (!hasWidth && !hasHeight) {
|
||||
attr.fheight = "";
|
||||
attr.fwidth = getDefaultWidth(plugin);
|
||||
attr.style = "excalidraw-svg";
|
||||
}
|
||||
|
||||
const alt = internalEmbedDiv.getAttribute("alt");
|
||||
processAltText(basename,alt,attr);
|
||||
/* const hasAttr =
|
||||
alt &&
|
||||
alt !== "" &&
|
||||
alt !== basename &&
|
||||
alt !== internalEmbedDiv.getAttribute("src") &&
|
||||
!alt.startsWith(attr.file.name + " > ");
|
||||
if (hasAttr) {
|
||||
//1:width, 2:height, 3:style 1 2 3
|
||||
const parts = alt.match(/(\d*%?)x?(\d*%?)\|?(.*)/);
|
||||
attr.fwidth = parts[1] ? parts[1] : getDefaultWidth(plugin);
|
||||
attr.fheight = parts[2];
|
||||
if (parts[3] != attr.fname) {
|
||||
attr.style = `excalidraw-svg${parts[3] ? `-${parts[3]}` : ""}`;
|
||||
}
|
||||
}*/
|
||||
|
||||
};
|
||||
|
||||
const createImgElement = async () => {
|
||||
setAttr();
|
||||
const imgDiv = await createImageDiv(attr);
|
||||
internalEmbedDiv.appendChild(imgDiv);
|
||||
};
|
||||
await createImgElement();
|
||||
const imgDiv = await processInternalEmbed(internalEmbedDiv,file);
|
||||
internalEmbedDiv.appendChild(imgDiv);
|
||||
|
||||
//timer to avoid the image flickering when the user is typing
|
||||
let timer: NodeJS.Timeout = null;
|
||||
@@ -420,11 +394,11 @@ const tmpObsidianWYSIWYG = async (
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
timer = setTimeout(async () => {
|
||||
timer = null;
|
||||
setAttr();
|
||||
internalEmbedDiv.empty();
|
||||
createImgElement();
|
||||
const imgDiv = await processInternalEmbed(internalEmbedDiv,file);
|
||||
internalEmbedDiv.appendChild(imgDiv);
|
||||
}, 500);
|
||||
});
|
||||
observer.observe(internalEmbedDiv, {
|
||||
@@ -459,7 +433,7 @@ export const markdownPostProcessor = async (
|
||||
return;
|
||||
}
|
||||
|
||||
await processInternalEmbeds(embeddedItems, ctx);
|
||||
await processReadingMode(embeddedItems, ctx);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,7 +34,7 @@ export class ScriptEngine {
|
||||
if (!path.endsWith(".svg")) {
|
||||
return;
|
||||
}
|
||||
const scriptFile = this.plugin.app.vault.getAbstractFileByPath(
|
||||
const scriptFile = app.vault.getAbstractFileByPath(
|
||||
getIMGFilename(path, "md"),
|
||||
);
|
||||
if (scriptFile && scriptFile instanceof TFile) {
|
||||
@@ -53,7 +53,7 @@ export class ScriptEngine {
|
||||
handleSvgFileChange(file.path);
|
||||
};
|
||||
this.plugin.registerEvent(
|
||||
this.plugin.app.vault.on("delete", deleteEventHandler),
|
||||
app.vault.on("delete", deleteEventHandler),
|
||||
);
|
||||
|
||||
const createEventHandler = async (file: TFile) => {
|
||||
@@ -67,7 +67,7 @@ export class ScriptEngine {
|
||||
handleSvgFileChange(file.path);
|
||||
};
|
||||
this.plugin.registerEvent(
|
||||
this.plugin.app.vault.on("create", createEventHandler),
|
||||
app.vault.on("create", createEventHandler),
|
||||
);
|
||||
|
||||
const renameEventHandler = async (file: TAbstractFile, oldPath: string) => {
|
||||
@@ -86,7 +86,7 @@ export class ScriptEngine {
|
||||
}
|
||||
};
|
||||
this.plugin.registerEvent(
|
||||
this.plugin.app.vault.on("rename", renameEventHandler),
|
||||
app.vault.on("rename", renameEventHandler),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,7 +101,6 @@ export class ScriptEngine {
|
||||
}
|
||||
|
||||
public getListofScripts(): TFile[] {
|
||||
const app = this.plugin.app;
|
||||
this.scriptPath = this.plugin.settings.scriptFolderPath;
|
||||
if (!app.vault.getAbstractFileByPath(this.scriptPath)) {
|
||||
this.scriptPath = null;
|
||||
@@ -140,10 +139,10 @@ export class ScriptEngine {
|
||||
|
||||
async addScriptIconToMap(scriptPath: string, name: string) {
|
||||
const svgFilePath = getIMGFilename(scriptPath, "svg");
|
||||
const file = this.plugin.app.vault.getAbstractFileByPath(svgFilePath);
|
||||
const file = app.vault.getAbstractFileByPath(svgFilePath);
|
||||
const svgString: string =
|
||||
file && file instanceof TFile
|
||||
? await this.plugin.app.vault.read(file)
|
||||
? await app.vault.read(file)
|
||||
: null;
|
||||
this.scriptIconMap = {
|
||||
...this.scriptIconMap,
|
||||
@@ -168,7 +167,7 @@ export class ScriptEngine {
|
||||
const view = app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
(async()=>{
|
||||
const script = await this.plugin.app.vault.read(f);
|
||||
const script = await app.vault.read(f);
|
||||
if(script) {
|
||||
this.executeScript(view, script, scriptName);
|
||||
}
|
||||
@@ -181,7 +180,6 @@ export class ScriptEngine {
|
||||
}
|
||||
|
||||
unloadScripts() {
|
||||
const app = this.plugin.app;
|
||||
const scripts = app.vault
|
||||
.getFiles()
|
||||
.filter((f: TFile) => f.path.startsWith(this.scriptPath));
|
||||
@@ -198,7 +196,6 @@ export class ScriptEngine {
|
||||
this.scriptIconMap = { ...this.scriptIconMap };
|
||||
this.updateToolPannels();
|
||||
|
||||
const app = this.plugin.app;
|
||||
const commandId = `${PLUGIN_ID}:${basename}`;
|
||||
// @ts-ignore
|
||||
if (!app.commands.commands[commandId]) {
|
||||
@@ -229,7 +226,7 @@ export class ScriptEngine {
|
||||
buttons?: [{ caption: string; action: Function }],
|
||||
) =>
|
||||
ScriptEngine.inputPrompt(
|
||||
this.plugin.app,
|
||||
app,
|
||||
header,
|
||||
placeholder,
|
||||
value,
|
||||
@@ -242,7 +239,7 @@ export class ScriptEngine {
|
||||
instructions?: Instruction[],
|
||||
) =>
|
||||
ScriptEngine.suggester(
|
||||
this.plugin.app,
|
||||
app,
|
||||
displayItems,
|
||||
items,
|
||||
hint,
|
||||
@@ -259,7 +256,7 @@ export class ScriptEngine {
|
||||
|
||||
private updateToolPannels() {
|
||||
const leaves =
|
||||
this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
leaves.forEach((leaf: WorkspaceLeaf) => {
|
||||
const excalidrawView = leaf.view as ExcalidrawView;
|
||||
excalidrawView.toolsPanelRef?.current?.updateScriptIconMap(
|
||||
|
||||
@@ -55,7 +55,7 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
|
||||
onChooseItem(item: TFile): void {
|
||||
switch (this.action) {
|
||||
case openDialogAction.openFile:
|
||||
this.plugin.openDrawing(item, this.onNewPane?"new-pane":"active-pane");
|
||||
this.plugin.openDrawing(item, this.onNewPane?"new-pane":"active-pane",true);
|
||||
break;
|
||||
case openDialogAction.insertLinkToDrawing:
|
||||
this.plugin.embedDrawing(item);
|
||||
|
||||
@@ -13,6 +13,7 @@ import ExcalidrawView from "../ExcalidrawView";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { sleep } from "../utils/Utils";
|
||||
import { getNewOrAdjacentLeaf } from "../utils/ObsidianUtils";
|
||||
import { checkAndCreateFolder, splitFolderAndFilename } from "src/utils/FileUtils";
|
||||
|
||||
export class Prompt extends Modal {
|
||||
private promptEl: HTMLInputElement;
|
||||
@@ -434,6 +435,8 @@ export class NewFileActions extends Modal {
|
||||
if (!this.path.match(/\.md$/)) {
|
||||
this.path = `${this.path}.md`;
|
||||
}
|
||||
const folderpath = splitFolderAndFilename(this.path).folderpath;
|
||||
checkAndCreateFolder(folderpath);
|
||||
const f = await this.app.vault.create(this.path, data);
|
||||
return f;
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@ export default {
|
||||
"Open an existing drawing - IN THE CURRENT ACTIVE PANE",
|
||||
TRANSCLUDE: "Transclude (embed) a drawing",
|
||||
TRANSCLUDE_MOST_RECENT: "Transclude (embed) the most recently edited drawing",
|
||||
TOGGLE_LEFTHANDED_MODE: "Toggle left-handed mode",
|
||||
NEW_IN_NEW_PANE: "Create a new drawing - IN A NEW PANE",
|
||||
NEW_IN_ACTIVE_PANE: "Create a new drawing - IN THE CURRENT ACTIVE PANE",
|
||||
NEW_IN_POPOUT_WINDOW: "Create a new drawing - IN A POPOUT WINDOW",
|
||||
@@ -41,7 +42,11 @@ export default {
|
||||
TOGGLE_LOCK: "Toggle Text Element edit RAW/PREVIEW",
|
||||
DELETE_FILE: "Delete selected Image or Markdown file from Obsidian Vault",
|
||||
INSERT_LINK_TO_ELEMENT:
|
||||
"Copy markdown link for selected element to clipboard",
|
||||
"Copy markdown link for selected element to clipboard. CTRL/CMD+Click to copy group link.",
|
||||
INSERT_LINK_TO_ELEMENT_GROUP:
|
||||
"Copy markdown link for selected element group to clipboard.",
|
||||
INSERT_LINK_TO_ELEMENT_NORMAL:
|
||||
"Copy markdown link for selected element to clipboard.",
|
||||
INSERT_LINK_TO_ELEMENT_ERROR: "Select a single element in the scene",
|
||||
INSERT_LINK_TO_ELEMENT_READY: "Link is READY and available on the clipboard",
|
||||
INSERT_LINK: "Insert link to file",
|
||||
|
||||
146
src/main.ts
146
src/main.ts
@@ -312,10 +312,10 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
//@ts-ignore
|
||||
win.MathJax.startup.pagePromise.then(async () => {
|
||||
//https://github.com/xldenis/obsidian-latex/blob/master/main.ts
|
||||
const file = self.app.vault.getAbstractFileByPath("preamble.sty");
|
||||
const file = app.vault.getAbstractFileByPath("preamble.sty");
|
||||
const preamble: string =
|
||||
file && file instanceof TFile
|
||||
? await self.app.vault.read(file)
|
||||
? await app.vault.read(file)
|
||||
: null;
|
||||
try {
|
||||
if (preamble) {
|
||||
@@ -348,7 +348,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
const self = this;
|
||||
this.app.workspace.onLayoutReady(() => {
|
||||
let leaf: WorkspaceLeaf;
|
||||
for (leaf of self.app.workspace.getLeavesOfType("markdown")) {
|
||||
for (leaf of app.workspace.getLeavesOfType("markdown")) {
|
||||
if (
|
||||
leaf.view instanceof MarkdownView &&
|
||||
self.isExcalidrawFile(leaf.view.file)
|
||||
@@ -455,7 +455,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
if (file) {
|
||||
await this.app.vault.modify(file as TFile, data);
|
||||
} else {
|
||||
await checkAndCreateFolder(this.app.vault, folder);
|
||||
await checkAndCreateFolder(folder);
|
||||
file = await this.app.vault.create(localPath, data);
|
||||
}
|
||||
return file;
|
||||
@@ -835,7 +835,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
).folder;
|
||||
const file = await this.createDrawing(filename, folder);
|
||||
await this.embedDrawing(file);
|
||||
this.openDrawing(file, location);
|
||||
this.openDrawing(file, location, true);
|
||||
};
|
||||
|
||||
this.addCommand({
|
||||
@@ -1044,20 +1044,62 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
this.addCommand({
|
||||
id: "insert-link-to-element",
|
||||
hotkeys: [{ modifiers: ["Ctrl" || "Meta", "Shift"], key: "k" }],
|
||||
name: t("INSERT_LINK_TO_ELEMENT"),
|
||||
name: t("INSERT_LINK_TO_ELEMENT_NORMAL"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
}
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
view.copyLinkToSelectedElementToClipboard();
|
||||
view.copyLinkToSelectedElementToClipboard(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "insert-link-to-element-group",
|
||||
name: t("INSERT_LINK_TO_ELEMENT_GROUP"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView))
|
||||
}
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if (view) {
|
||||
view.copyLinkToSelectedElementToClipboard(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "toggle-lefthanded-mode",
|
||||
name: t("TOGGLE_LEFTHANDED_MODE"),
|
||||
checkCallback: (checking: boolean) => {
|
||||
if (checking) {
|
||||
if(this.app.workspace.getActiveViewOfType(ExcalidrawView)) {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
const api = view?.excalidrawAPI;
|
||||
if(!api) return false;
|
||||
const st = api.getAppState();
|
||||
if(!st.trayModeEnabled) return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
(async()=>{
|
||||
await this.loadSettings();
|
||||
this.settings.isLeftHanded = !this.settings.isLeftHanded;
|
||||
this.saveSettings();
|
||||
setLeftHandedMode(this.settings.isLeftHanded);
|
||||
})()
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "insert-image",
|
||||
name: t("INSERT_IMAGE"),
|
||||
@@ -1358,7 +1400,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
"markdown"
|
||||
) {
|
||||
// Then check for the excalidraw frontMatterKey
|
||||
const cache = self.app.metadataCache.getCache(state.state.file);
|
||||
const cache = app.metadataCache.getCache(state.state.file);
|
||||
|
||||
if (cache?.frontmatter && cache.frontmatter[FRONTMATTER_KEY]) {
|
||||
// If we have it, force the view type to excalidraw
|
||||
@@ -1379,43 +1421,6 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
// Add a menu item to go back to Excalidraw view
|
||||
/*this.register(
|
||||
around(MarkdownView.prototype, {
|
||||
onPaneMenu(next) {
|
||||
return function (menu: Menu) {
|
||||
const file = this.file;
|
||||
const cache = file
|
||||
? self.app.metadataCache.getFileCache(file)
|
||||
: null;
|
||||
|
||||
if (
|
||||
!file ||
|
||||
!cache?.frontmatter ||
|
||||
!cache.frontmatter[FRONTMATTER_KEY]
|
||||
) {
|
||||
return next.call(this, menu);
|
||||
}
|
||||
|
||||
menu
|
||||
.addItem((item) => {
|
||||
item
|
||||
.setTitle(t("OPEN_AS_EXCALIDRAW"))
|
||||
.setIcon(ICON_NAME)
|
||||
.setSection("pane")
|
||||
.onClick(() => {
|
||||
self.excalidrawFileModes[this.leaf.id || file.path] =
|
||||
VIEW_TYPE_EXCALIDRAW;
|
||||
self.setExcalidrawView(this.leaf);
|
||||
});
|
||||
})
|
||||
.addSeparator();
|
||||
next.call(this, menu);
|
||||
};
|
||||
},
|
||||
}),
|
||||
);*/
|
||||
}
|
||||
|
||||
private popScope: Function = null;
|
||||
@@ -1439,19 +1444,19 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
[".svg", ".png", ".excalidraw"].forEach(async (ext: string) => {
|
||||
const oldIMGpath = getIMGPathFromExcalidrawFile(oldPath, ext);
|
||||
const imgFile = self.app.vault.getAbstractFileByPath(
|
||||
const imgFile = app.vault.getAbstractFileByPath(
|
||||
normalizePath(oldIMGpath),
|
||||
);
|
||||
if (imgFile && imgFile instanceof TFile) {
|
||||
const newIMGpath = getIMGPathFromExcalidrawFile(file.path, ext);
|
||||
await self.app.fileManager.renameFile(imgFile, newIMGpath);
|
||||
await app.fileManager.renameFile(imgFile, newIMGpath);
|
||||
}
|
||||
});
|
||||
};
|
||||
self.registerEvent(self.app.vault.on("rename", renameEventHandler));
|
||||
self.registerEvent(app.vault.on("rename", renameEventHandler));
|
||||
|
||||
const modifyEventHandler = async (file: TFile) => {
|
||||
const leaves = self.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
const leaves = app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
leaves.forEach(async (leaf: WorkspaceLeaf) => {
|
||||
const excalidrawView = leaf.view as ExcalidrawView;
|
||||
if (
|
||||
@@ -1484,7 +1489,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
});
|
||||
};
|
||||
self.registerEvent(self.app.vault.on("modify", modifyEventHandler));
|
||||
self.registerEvent(app.vault.on("modify", modifyEventHandler));
|
||||
|
||||
//watch file delete and delete corresponding .svg and .png
|
||||
const deleteEventHandler = async (file: TFile) => {
|
||||
@@ -1499,7 +1504,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
|
||||
//close excalidraw view where this file is open
|
||||
const leaves = self.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
const leaves = app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
for (let i = 0; i < leaves.length; i++) {
|
||||
if ((leaves[i].view as ExcalidrawView).file.path == file.path) {
|
||||
await leaves[i].setViewState({
|
||||
@@ -1514,27 +1519,27 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
setTimeout(() => {
|
||||
[".svg", ".png", ".excalidraw"].forEach(async (ext: string) => {
|
||||
const imgPath = getIMGPathFromExcalidrawFile(file.path, ext);
|
||||
const imgFile = self.app.vault.getAbstractFileByPath(
|
||||
const imgFile = app.vault.getAbstractFileByPath(
|
||||
normalizePath(imgPath),
|
||||
);
|
||||
if (imgFile && imgFile instanceof TFile) {
|
||||
await self.app.vault.delete(imgFile);
|
||||
await app.vault.delete(imgFile);
|
||||
}
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
self.registerEvent(self.app.vault.on("delete", deleteEventHandler));
|
||||
self.registerEvent(app.vault.on("delete", deleteEventHandler));
|
||||
|
||||
//save open drawings when user quits the application
|
||||
//Removing because it is not guaranteed to run, and frequently gets terminated mid flight, causing file consistency issues
|
||||
/*const quitEventHandler = async () => {
|
||||
const leaves = self.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
const leaves = app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
for (let i = 0; i < leaves.length; i++) {
|
||||
await (leaves[i].view as ExcalidrawView).save(true);
|
||||
}
|
||||
};
|
||||
self.registerEvent(self.app.workspace.on("quit", quitEventHandler));*/
|
||||
self.registerEvent(app.workspace.on("quit", quitEventHandler));*/
|
||||
|
||||
//save Excalidraw leaf and update embeds when switching to another leaf
|
||||
const activeLeafChangeEventHandler = async (leaf: WorkspaceLeaf) => {
|
||||
@@ -1603,7 +1608,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
};
|
||||
self.registerEvent(
|
||||
self.app.workspace.on(
|
||||
app.workspace.on(
|
||||
"active-leaf-change",
|
||||
activeLeafChangeEventHandler,
|
||||
),
|
||||
@@ -1611,7 +1616,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
self.addFileSaveTriggerEventHandlers();
|
||||
|
||||
const metaCache: MetadataCache = self.app.metadataCache;
|
||||
const metaCache: MetadataCache = app.metadataCache;
|
||||
//@ts-ignore
|
||||
metaCache.getCachedFiles().forEach((filename: string) => {
|
||||
const fm = metaCache.getCache(filename)?.frontmatter;
|
||||
@@ -1620,7 +1625,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
filename.match(/\.excalidraw$/)
|
||||
) {
|
||||
self.updateFileCache(
|
||||
self.app.vault.getAbstractFileByPath(filename) as TFile,
|
||||
app.vault.getAbstractFileByPath(filename) as TFile,
|
||||
fm,
|
||||
);
|
||||
}
|
||||
@@ -1864,7 +1869,9 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
|
||||
public openDrawing(
|
||||
drawingFile: TFile,
|
||||
location: "active-pane"|"new-pane"|"popout-window"
|
||||
location: "active-pane"|"new-pane"|"popout-window",
|
||||
active: boolean = false,
|
||||
subpath?: string
|
||||
) {
|
||||
let leaf: WorkspaceLeaf;
|
||||
if(location === "popout-window") {
|
||||
@@ -1878,10 +1885,17 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
leaf.setViewState({
|
||||
leaf.openFile(
|
||||
drawingFile,
|
||||
!subpath || subpath === ""
|
||||
? {active}
|
||||
: { active, eState: { subpath } }
|
||||
);
|
||||
|
||||
/* leaf.setViewState({
|
||||
type: VIEW_TYPE_EXCALIDRAW,
|
||||
state: { file: drawingFile.path },
|
||||
});
|
||||
state: { file: drawingFile.path, eState: {subpath}},
|
||||
});*/
|
||||
}
|
||||
|
||||
public async getBlankDrawing(): Promise<string> {
|
||||
@@ -1960,7 +1974,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
const folderpath = normalizePath(
|
||||
foldername ? foldername : this.settings.folder,
|
||||
);
|
||||
await checkAndCreateFolder(this.app.vault, folderpath); //create folder if it does not exist
|
||||
await checkAndCreateFolder(folderpath); //create folder if it does not exist
|
||||
const fname = getNewUniqueFilepath(this.app.vault, filename, folderpath);
|
||||
const file = await this.app.vault.create(
|
||||
fname,
|
||||
@@ -1987,7 +2001,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
initData?: string,
|
||||
): Promise<string> {
|
||||
const file = await this.createDrawing(filename, foldername, initData);
|
||||
this.openDrawing(file, location);
|
||||
this.openDrawing(file, location, true);
|
||||
return file.path;
|
||||
}
|
||||
|
||||
@@ -2039,7 +2053,7 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
}
|
||||
filename = `${filename}.excalidrawlib`;
|
||||
const folderpath = normalizePath(this.settings.folder);
|
||||
await checkAndCreateFolder(this.app.vault, folderpath); //create folder if it does not exist
|
||||
await checkAndCreateFolder(folderpath); //create folder if it does not exist
|
||||
const fname = getNewUniqueFilepath(
|
||||
this.app.vault,
|
||||
filename,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Notice, TFile } from "obsidian";
|
||||
import * as React from "react";
|
||||
import { ActionButton } from "./ActionButton";
|
||||
import { ICONS } from "./ActionIcons";
|
||||
import { SCRIPT_INSTALL_FOLDER } from "../Constants";
|
||||
import { SCRIPT_INSTALL_FOLDER, CTRL_OR_CMD } from "../Constants";
|
||||
import { insertLaTeXToView, search } from "../ExcalidrawAutomate";
|
||||
import ExcalidrawView, { TextMode } from "../ExcalidrawView";
|
||||
import { t } from "../lang/helpers";
|
||||
@@ -464,8 +464,8 @@ export class ToolsPanel extends React.Component<PanelProps, PanelState> {
|
||||
<ActionButton
|
||||
key={"link-to-element"}
|
||||
title={t("INSERT_LINK_TO_ELEMENT")}
|
||||
action={() => {
|
||||
this.props.view.copyLinkToSelectedElementToClipboard();
|
||||
action={(e:React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
|
||||
this.props.view.copyLinkToSelectedElementToClipboard(e[CTRL_OR_CMD]);
|
||||
}}
|
||||
icon={ICONS.copyElementLink}
|
||||
view={this.props.view}
|
||||
|
||||
@@ -219,7 +219,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
|
||||
this.plugin.saveSettings();
|
||||
if (this.requestReloadDrawings) {
|
||||
const exs =
|
||||
this.plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
|
||||
for (const v of exs) {
|
||||
if (v.view instanceof ExcalidrawView) {
|
||||
await v.view.save(false);
|
||||
|
||||
@@ -120,7 +120,8 @@ export function getEmbedFilename(
|
||||
* Open or create a folderpath if it does not exist
|
||||
* @param folderpath
|
||||
*/
|
||||
export async function checkAndCreateFolder(vault: Vault, folderpath: string) {
|
||||
export async function checkAndCreateFolder(folderpath: string) {
|
||||
const vault = app.vault;
|
||||
folderpath = normalizePath(folderpath);
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/658
|
||||
//@ts-ignore
|
||||
|
||||
@@ -144,7 +144,7 @@ export const getAttachmentsFolderAndFilePath = async (
|
||||
if (!folder || folder === "/") {
|
||||
folder = "";
|
||||
}
|
||||
await checkAndCreateFolder(app.vault, folder);
|
||||
await checkAndCreateFolder(folder);
|
||||
return {
|
||||
folder,
|
||||
filepath: normalizePath(
|
||||
|
||||
@@ -576,8 +576,8 @@ export const getEmbeddedFilenameParts = (fname:string):{
|
||||
linkpartReference: string,
|
||||
linkpartAlias: string
|
||||
} => {
|
||||
// 0 1 23 4 5 6 7 8
|
||||
const parts = fname?.match(/([^#\^]*)((#\^)(group_)?([^\|]*)|(#)([^\^\|]*))(.*)/);
|
||||
// 0 1 23 4 5 6 7 8 9
|
||||
const parts = fname?.match(/([^#\^]*)((#\^)(group=)?([^\|]*)|(#)(group=)?([^\^\|]*))(.*)/);
|
||||
if(!parts) {
|
||||
return {
|
||||
filepath: fname,
|
||||
@@ -593,12 +593,12 @@ export const getEmbeddedFilenameParts = (fname:string):{
|
||||
return {
|
||||
filepath: parts[1],
|
||||
hasBlockref: Boolean(parts[3]),
|
||||
hasGroupref: Boolean(parts[4]),
|
||||
hasGroupref: Boolean(parts[4]) || Boolean(parts[7]),
|
||||
blockref: parts[5],
|
||||
hasSectionref: Boolean(parts[6]),
|
||||
sectionref: parts[7],
|
||||
sectionref: parts[8],
|
||||
linkpartReference: parts[2],
|
||||
linkpartAlias: parts[8]
|
||||
linkpartAlias: parts[9]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,6 +607,7 @@ export const errorlog = (data: {}) => {
|
||||
};
|
||||
|
||||
export const sleep = async (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
export const awaitNextAnimationFrame = async () => new Promise(requestAnimationFrame);
|
||||
|
||||
export const log = console.log.bind(window.console);
|
||||
export const debug = console.log.bind(window.console);
|
||||
|
||||
@@ -138,7 +138,11 @@ li[data-testid] {
|
||||
}
|
||||
|
||||
.excalidraw-prompt-center {
|
||||
text-align: center;
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.excalidraw-prompt-center button {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.excalidraw-prompt-center.filepath {
|
||||
|
||||
Reference in New Issue
Block a user