mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
4 Commits
2.4.0-beta
...
2.4.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
013279ab60 | ||
|
|
06193b6d49 | ||
|
|
ac6f4af5d6 | ||
|
|
0f9dafb01d |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.4.0-beta-2",
|
||||
"version": "2.4.0-beta-4",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
"authorUrl": "https://zsolt.blog",
|
||||
"authorUrl": "https://www.zsolt.blog",
|
||||
"fundingUrl": "https://ko-fi.com/zsolt",
|
||||
"helpUrl": "https://github.com/zsviczian/obsidian-excalidraw-plugin#readme",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@zsviczian/excalidraw": "0.17.1-obsidian-38",
|
||||
"@zsviczian/excalidraw": "0.17.1-obsidian-39",
|
||||
"chroma-js": "^2.4.2",
|
||||
"clsx": "^2.0.0",
|
||||
"colormaster": "^1.2.1",
|
||||
|
||||
@@ -15,7 +15,7 @@ import cssnano from 'cssnano';
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
const DIST_FOLDER = 'dist';
|
||||
const DIST_FOLDER = 'dist';
|
||||
const isProd = (process.env.NODE_ENV === "production");
|
||||
const isLib = (process.env.NODE_ENV === "lib");
|
||||
console.log(`Running: ${process.env.NODE_ENV}`);
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
ExcalidrawFrameElement,
|
||||
ExcalidrawTextContainer,
|
||||
} from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||
import { MimeType } from "./EmbeddedFileLoader";
|
||||
import { Editor, normalizePath, Notice, OpenViewState, RequestUrlResponse, TFile, TFolder, WorkspaceLeaf } from "obsidian";
|
||||
import * as obsidian_module from "obsidian";
|
||||
import ExcalidrawView, { ExportSettings, TextMode, getTextMode } from "src/ExcalidrawView";
|
||||
@@ -1026,6 +1027,22 @@ export class ExcalidrawAutomate {
|
||||
return id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add elements to frame
|
||||
* @param frameId
|
||||
* @param elementIDs to add
|
||||
* @returns void
|
||||
*/
|
||||
addElementsToFrame(frameId: string, elementIDs: string[]):void {
|
||||
if(!this.getElement(frameId)) return;
|
||||
elementIDs.forEach(elID => {
|
||||
const el = this.getElement(elID);
|
||||
if(el) {
|
||||
el.frameId = frameId;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param topX
|
||||
@@ -1571,6 +1588,26 @@ export class ExcalidrawAutomate {
|
||||
return id;
|
||||
};
|
||||
|
||||
/**
|
||||
* returns the base64 dataURL of the LaTeX equation rendered as an SVG
|
||||
* @param tex The LaTeX equation as string
|
||||
* @param scale of the image, default value is 4
|
||||
* @returns
|
||||
*/
|
||||
//https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1930
|
||||
async tex2dataURL(
|
||||
tex: string,
|
||||
scale: number = 4 // Default scale value, adjust as needed
|
||||
): Promise<{
|
||||
mimeType: MimeType;
|
||||
fileId: FileId;
|
||||
dataURL: DataURL;
|
||||
created: number;
|
||||
size: { height: number; width: number };
|
||||
}> {
|
||||
return await tex2dataURL(tex,scale);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param objectA
|
||||
@@ -1896,15 +1933,16 @@ export class ExcalidrawAutomate {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param includeFrameChildren
|
||||
* @returns
|
||||
*/
|
||||
getViewSelectedElements(): any[] {
|
||||
getViewSelectedElements(includeFrameChildren:boolean = true): any[] {
|
||||
//@ts-ignore
|
||||
if (!this.targetView || !this.targetView?._loaded) {
|
||||
errorMessage("targetView not set", "getViewSelectedElements()");
|
||||
return [];
|
||||
}
|
||||
return this.targetView.getViewSelectedElements();
|
||||
return this.targetView.getViewSelectedElements(includeFrameChildren);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2396,24 +2434,44 @@ export class ExcalidrawAutomate {
|
||||
* @param elements - typically all the non-deleted elements in the scene
|
||||
* @returns
|
||||
*/
|
||||
getElementsInTheSameGroupWithElement(element: ExcalidrawElement, elements: ExcalidrawElement[]): ExcalidrawElement[] {
|
||||
getElementsInTheSameGroupWithElement(
|
||||
element: ExcalidrawElement,
|
||||
elements: ExcalidrawElement[],
|
||||
includeFrameElements: boolean = false,
|
||||
): ExcalidrawElement[] {
|
||||
if(!element || !elements) return [];
|
||||
const container = (element.type === "text" && element.containerId)
|
||||
? elements.filter(el=>el.id === element.containerId)
|
||||
: [];
|
||||
if(element.groupIds.length === 0) {
|
||||
if(includeFrameElements && element.type === "frame") {
|
||||
return this.getElementsInFrame(element,elements,true);
|
||||
}
|
||||
if(container.length === 1) return [element,container[0]];
|
||||
return [element];
|
||||
}
|
||||
|
||||
if(container.length === 1) {
|
||||
return elements.filter(el=>
|
||||
el.groupIds.some(id=>element.groupIds.includes(id)) ||
|
||||
el === container[0]
|
||||
);
|
||||
const conditionFN = container.length === 1
|
||||
? (el: ExcalidrawElement) => el.groupIds.some(id=>element.groupIds.includes(id)) || el === container[0]
|
||||
: (el: ExcalidrawElement) => el.groupIds.some(id=>element.groupIds.includes(id));
|
||||
|
||||
if(!includeFrameElements) {
|
||||
return elements.filter(el=>conditionFN(el));
|
||||
} else {
|
||||
//I use the set and the filter at the end to preserve scene layer seqeuence
|
||||
//adding frames could potentially mess up the sequence otherwise
|
||||
const elementIDs = new Set<string>();
|
||||
elements
|
||||
.filter(el=>conditionFN(el))
|
||||
.forEach(el=>{
|
||||
if(el.type === "frame") {
|
||||
this.getElementsInFrame(el,elements,true).forEach(el=>elementIDs.add(el.id))
|
||||
} else {
|
||||
elementIDs.add(el.id);
|
||||
}
|
||||
});
|
||||
return elements.filter(el=>elementIDs.has(el.id));
|
||||
}
|
||||
|
||||
return elements.filter(el=>el.groupIds.some(id=>element.groupIds.includes(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2738,7 +2796,7 @@ export async function initExcalidrawAutomate(
|
||||
function normalizeLinePoints(
|
||||
points: [x: number, y: number][],
|
||||
//box: { x: number; y: number; w: number; h: number },
|
||||
) {
|
||||
): number[][] {
|
||||
const p = [];
|
||||
const [x, y] = points[0];
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
@@ -2747,7 +2805,9 @@ function normalizeLinePoints(
|
||||
return p;
|
||||
}
|
||||
|
||||
function getLineBox(points: [x: number, y: number][]) {
|
||||
function getLineBox(
|
||||
points: [x: number, y: number][]
|
||||
):{x:number, y:number, w: number, h:number} {
|
||||
const [x1, y1, x2, y2] = estimateLineBound(points);
|
||||
return {
|
||||
x: x1,
|
||||
@@ -2757,11 +2817,11 @@ function getLineBox(points: [x: number, y: number][]) {
|
||||
};
|
||||
}
|
||||
|
||||
function getFontFamily(id: number) {
|
||||
getFontFamilyString({fontFamily:id})
|
||||
function getFontFamily(id: number):string {
|
||||
return getFontFamilyString({fontFamily:id})
|
||||
}
|
||||
|
||||
export async function initFonts() {
|
||||
export async function initFonts():Promise<void> {
|
||||
await excalidrawLib.registerFontsInCSS();
|
||||
const fonts = excalidrawLib.getFontFamilies();
|
||||
for(let i=0;i<fonts.length;i++) {
|
||||
@@ -2774,7 +2834,7 @@ export function _measureText(
|
||||
fontSize: number,
|
||||
fontFamily: number,
|
||||
lineHeight: number,
|
||||
) {
|
||||
): {w: number, h:number} {
|
||||
//following odd error with mindmap on iPad while synchornizing with desktop.
|
||||
if (!fontSize) {
|
||||
fontSize = 20;
|
||||
@@ -2873,7 +2933,7 @@ async function getTemplate(
|
||||
? 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)
|
||||
groupElements = plugin.ea.getElementsInTheSameGroupWithElement(el[0],scene.elements,true)
|
||||
}
|
||||
}
|
||||
if(filenameParts.hasFrameref || filenameParts.hasClippedFrameref) {
|
||||
@@ -2932,7 +2992,7 @@ export async function createPNG(
|
||||
depth: number,
|
||||
padding?: number,
|
||||
imagesDict?: any,
|
||||
) {
|
||||
): Promise<Blob> {
|
||||
if (!loader) {
|
||||
loader = new EmbeddedFilesLoader(plugin);
|
||||
}
|
||||
@@ -3016,7 +3076,7 @@ export const updateElementLinksToObsidianLinks = ({elements, hostFile}:{
|
||||
})
|
||||
}
|
||||
|
||||
function addFilterToForeignObjects(svg:SVGSVGElement) {
|
||||
function addFilterToForeignObjects(svg:SVGSVGElement):void {
|
||||
const foreignObjects = svg.querySelectorAll("foreignObject");
|
||||
foreignObjects.forEach((foreignObject) => {
|
||||
foreignObject.setAttribute("filter", THEME_FILTER);
|
||||
@@ -3165,7 +3225,7 @@ export function repositionElementsToCursor(
|
||||
return restore({elements}, null, null).elements;
|
||||
}
|
||||
|
||||
function errorMessage(message: string, source: string) {
|
||||
function errorMessage(message: string, source: string):void {
|
||||
switch (message) {
|
||||
case "targetView not set":
|
||||
errorlog({
|
||||
|
||||
@@ -5681,9 +5681,14 @@ export default class ExcalidrawView extends TextFileView {
|
||||
return api.getSceneElements();
|
||||
}
|
||||
|
||||
public getViewSelectedElements(): ExcalidrawElement[] {
|
||||
/**
|
||||
*
|
||||
* @param deepSelect: if set to true, child elements of the selected frame will also be selected
|
||||
* @returns
|
||||
*/
|
||||
public getViewSelectedElements(includFrameChildren: boolean = true): ExcalidrawElement[] {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.getViewSelectedElements, "ExcalidrawView.getViewSelectedElements");
|
||||
const api = this.excalidrawAPI;
|
||||
const api = this.excalidrawAPI as ExcalidrawImperativeAPI;
|
||||
if (!api) {
|
||||
return [];
|
||||
}
|
||||
@@ -5695,6 +5700,9 @@ export default class ExcalidrawView extends TextFileView {
|
||||
if (!selectedElementsKeys) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const elementIDs = new Set<string>();
|
||||
|
||||
const elements: ExcalidrawElement[] = api
|
||||
.getSceneElements()
|
||||
.filter((e: any) => selectedElementsKeys.includes(e.id));
|
||||
@@ -5712,15 +5720,27 @@ export default class ExcalidrawView extends TextFileView {
|
||||
.map((be) => be.id)[0],
|
||||
);
|
||||
|
||||
const elementIDs = elements
|
||||
.map((el) => el.id)
|
||||
.concat(containerBoundTextElmenetsReferencedInElements);
|
||||
if(includFrameChildren && elements.some(el=>el.type === "frame")) {
|
||||
elements.filter(el=>el.type === "frame").forEach(frameEl => {
|
||||
api.getSceneElements()
|
||||
.filter(el=>el.frameId === frameEl.id)
|
||||
.forEach(el=>elementIDs.add(el.id))
|
||||
})
|
||||
}
|
||||
|
||||
elements.forEach(el=>elementIDs.add(el.id));
|
||||
containerBoundTextElmenetsReferencedInElements.forEach(id=>elementIDs.add(id));
|
||||
|
||||
return api
|
||||
.getSceneElements()
|
||||
.filter((el: ExcalidrawElement) => elementIDs.contains(el.id));
|
||||
.filter((el: ExcalidrawElement) => elementIDs.has(el.id));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param prefix - defines the default button.
|
||||
* @returns
|
||||
*/
|
||||
public async copyLinkToSelectedElementToClipboard(prefix:string) {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.copyLinkToSelectedElementToClipboard, "ExcalidrawView.copyLinkToSelectedElementToClipboard", prefix);
|
||||
const elements = this.getViewSelectedElements();
|
||||
@@ -5747,58 +5767,59 @@ export default class ExcalidrawView extends TextFileView {
|
||||
: this.plugin.ea.getLargestElement(elements).id;
|
||||
}
|
||||
|
||||
const isFrame = elements.some(el=>el.id === elementId && el.type==="frame");
|
||||
const frames = elements.filter(el=>el.type==="frame");
|
||||
const hasFrame = frames.length === 1;
|
||||
const hasGroup = elements.some(el=>el.groupIds && el.groupIds.length>0);
|
||||
|
||||
let button = {
|
||||
area: {caption: "Area", action:()=>{prefix="area="; return;}},
|
||||
link: {caption: "Link", action:()=>{prefix="";return}},
|
||||
group: {caption: "Group", action:()=>{prefix="group="; return;}},
|
||||
frame: {caption: "Frame", action:()=>{prefix="frame="; elementId = frames[0].id; return;}},
|
||||
clippedframe: {caption: "Clipped Frame", action:()=>{prefix="clippedframe="; ; elementId = frames[0].id; return;}},
|
||||
}
|
||||
|
||||
let buttons = [];
|
||||
if(isFrame) {
|
||||
switch(prefix) {
|
||||
case "clippedframe=":
|
||||
buttons = [
|
||||
{caption: "Clipped Frame", action:()=>{prefix="clippedframe="; return;}},
|
||||
{caption: "Frame", action:()=>{prefix="frame="; return;}},
|
||||
{caption: "Link", action:()=>{prefix="";return}},
|
||||
];
|
||||
break;
|
||||
case "area=":
|
||||
case "group=":
|
||||
case "frame=":
|
||||
buttons = [
|
||||
{caption: "Frame", action:()=>{prefix="frame="; return;}},
|
||||
{caption: "Clipped Frame", action:()=>{prefix="clippedframe="; return;}},
|
||||
{caption: "Link", action:()=>{prefix="";return}},
|
||||
];
|
||||
break;
|
||||
default:
|
||||
buttons = [
|
||||
{caption: "Link", action:()=>{prefix="";return}},
|
||||
{caption: "Frame", action:()=>{prefix="frame="; return;}},
|
||||
{caption: "Clipped Frame", action:()=>{prefix="clippedframe="; return;}},
|
||||
]
|
||||
}
|
||||
|
||||
} else {
|
||||
switch(prefix) {
|
||||
case "area=":
|
||||
buttons = [
|
||||
{caption: "Area", action:()=>{prefix="area="; return;}},
|
||||
{caption: "Link", action:()=>{prefix="";return}},
|
||||
{caption: "Group", action:()=>{prefix="group="; return;}},
|
||||
];
|
||||
break;
|
||||
case "group=":
|
||||
buttons = [
|
||||
{caption: "Group", action:()=>{prefix="group="; return;}},
|
||||
{caption: "Link", action:()=>{prefix="";return}},
|
||||
{caption: "Area", action:()=>{prefix="area="; return;}},
|
||||
];
|
||||
break;
|
||||
default:
|
||||
buttons = [
|
||||
{caption: "Link", action:()=>{prefix="";return}},
|
||||
{caption: "Area", action:()=>{prefix="area="; return;}},
|
||||
{caption: "Group", action:()=>{prefix="group="; return;}},
|
||||
]
|
||||
}
|
||||
switch(prefix) {
|
||||
case "area=":
|
||||
buttons = [
|
||||
button.area,
|
||||
button.link,
|
||||
...hasGroup ? [button.group] : [],
|
||||
...hasFrame ? [button.frame, button.clippedframe] : [],
|
||||
];
|
||||
break;
|
||||
case "group=":
|
||||
buttons = [
|
||||
...hasGroup ? [button.group] : [],
|
||||
button.link,
|
||||
button.area,
|
||||
...hasFrame ? [button.frame, button.clippedframe] : [],
|
||||
];
|
||||
break;
|
||||
case "frame=":
|
||||
buttons = [
|
||||
...hasFrame ? [button.frame, button.clippedframe] : [],
|
||||
...hasGroup ? [button.group] : [],
|
||||
button.link,
|
||||
button.area,
|
||||
];
|
||||
break;
|
||||
case "clippedframe=":
|
||||
buttons = [
|
||||
...hasFrame ? [button.clippedframe, button.frame] : [],
|
||||
...hasGroup ? [button.group] : [],
|
||||
button.link,
|
||||
button.area,
|
||||
];
|
||||
break;
|
||||
default:
|
||||
buttons = [
|
||||
{caption: "Link", action:()=>{prefix="";return}},
|
||||
{caption: "Area", action:()=>{prefix="area="; return;}},
|
||||
{caption: "Group", action:()=>{prefix="group="; return;}},
|
||||
...hasFrame ? [button.frame, button.clippedframe] : [],
|
||||
]
|
||||
}
|
||||
|
||||
const alias = await ScriptEngine.inputPrompt(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
App,
|
||||
MarkdownPostProcessorContext,
|
||||
MetadataCache,
|
||||
PaneType,
|
||||
@@ -25,7 +26,7 @@ import { getParentOfClass, isObsidianThemeDark, getFileCSSClasses } from "./util
|
||||
import { linkClickModifierType } from "./utils/ModifierkeyHelper";
|
||||
import { ImageKey, imageCache } from "./utils/ImageCache";
|
||||
import { FILENAMEPARTS, PreviewImageType } from "./utils/UtilTypes";
|
||||
import { CustomMutationObserver, DEBUGGING } from "./utils/DebugHelper";
|
||||
import { CustomMutationObserver, debug, DEBUGGING } from "./utils/DebugHelper";
|
||||
import { getExcalidrawFileForwardLinks } from "./utils/ExcalidrawViewUtils";
|
||||
import { linkPrompt } from "./dialogs/Prompt";
|
||||
|
||||
@@ -38,8 +39,11 @@ interface imgElementAttributes {
|
||||
}
|
||||
|
||||
let plugin: ExcalidrawPlugin;
|
||||
let app: App;
|
||||
let vault: Vault;
|
||||
let metadataCache: MetadataCache;
|
||||
const DEBUGGING_MPP = true;
|
||||
|
||||
|
||||
const getDefaultWidth = (plugin: ExcalidrawPlugin): string => {
|
||||
const width = parseInt(plugin.settings.width);
|
||||
@@ -60,8 +64,9 @@ const getDefaultHeight = (plugin: ExcalidrawPlugin): string => {
|
||||
|
||||
export const initializeMarkdownPostProcessor = (p: ExcalidrawPlugin) => {
|
||||
plugin = p;
|
||||
vault = p.app.vault;
|
||||
metadataCache = p.app.metadataCache;
|
||||
app = plugin.app;
|
||||
vault = app.vault;
|
||||
metadataCache = app.metadataCache;
|
||||
};
|
||||
|
||||
const _getPNG = async ({imgAttributes,filenameParts,theme,cacheReady,img,file,exportSettings,loader}:{
|
||||
@@ -74,6 +79,7 @@ const _getPNG = async ({imgAttributes,filenameParts,theme,cacheReady,img,file,ex
|
||||
exportSettings: ExportSettings,
|
||||
loader: EmbeddedFilesLoader,
|
||||
}):Promise<HTMLImageElement> => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(_getPNG, `MarkdownPostProcessor.ts > _getPNG`);
|
||||
const width = parseInt(imgAttributes.fwidth);
|
||||
const scale = width >= 2400
|
||||
? 5
|
||||
@@ -140,6 +146,7 @@ const setStyle = ({element,imgAttributes,onCanvas}:{
|
||||
onCanvas: boolean,
|
||||
}
|
||||
) => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(setStyle, `MarkdownPostProcessor.ts > setStyle`);
|
||||
let style = "";
|
||||
if(imgAttributes.fwidth) {
|
||||
style = `max-width:${imgAttributes.fwidth}${imgAttributes.fwidth.match(/\d$/) ? "px":""}; `; //width:100%;`; //removed !important https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/886
|
||||
@@ -171,6 +178,7 @@ const _getSVGIMG = async ({filenameParts,theme,cacheReady,img,file,exportSetting
|
||||
exportSettings: ExportSettings,
|
||||
loader: EmbeddedFilesLoader,
|
||||
}):Promise<HTMLImageElement> => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(_getSVGIMG, `MarkdownPostProcessor.ts > _getSVGIMG`);
|
||||
exportSettings.skipInliningFonts = false;
|
||||
const cacheKey = {
|
||||
...filenameParts,
|
||||
@@ -238,6 +246,7 @@ const _getSVGNative = async ({filenameParts,theme,cacheReady,containerElement,fi
|
||||
exportSettings: ExportSettings,
|
||||
loader: EmbeddedFilesLoader,
|
||||
}):Promise<HTMLDivElement> => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(_getSVGNative, `MarkdownPostProcessor.ts > _getSVGNative`);
|
||||
exportSettings.skipInliningFonts = false;
|
||||
const cacheKey = {
|
||||
...filenameParts,
|
||||
@@ -300,6 +309,7 @@ const getIMG = async (
|
||||
imgAttributes: imgElementAttributes,
|
||||
onCanvas: boolean = false,
|
||||
): Promise<HTMLImageElement | HTMLDivElement> => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(getIMG, `MarkdownPostProcessor.ts > getIMG`, imgAttributes);
|
||||
let file = imgAttributes.file;
|
||||
if (!imgAttributes.file) {
|
||||
const f = vault.getAbstractFileByPath(imgAttributes.fname?.split("#")[0]);
|
||||
@@ -347,22 +357,23 @@ const getIMG = async (
|
||||
case PreviewImageType.PNG: {
|
||||
const img = createEl("img");
|
||||
setStyle({element:img,imgAttributes,onCanvas});
|
||||
return _getPNG({imgAttributes,filenameParts,theme,cacheReady,img,file,exportSettings,loader});
|
||||
return await _getPNG({imgAttributes,filenameParts,theme,cacheReady,img,file,exportSettings,loader});
|
||||
}
|
||||
case PreviewImageType.SVGIMG: {
|
||||
const img = createEl("img");
|
||||
setStyle({element:img,imgAttributes,onCanvas});
|
||||
return _getSVGIMG({filenameParts,theme,cacheReady,img,file,exportSettings,loader});
|
||||
return await _getSVGIMG({filenameParts,theme,cacheReady,img,file,exportSettings,loader});
|
||||
}
|
||||
case PreviewImageType.SVG: {
|
||||
const img = createEl("div");
|
||||
setStyle({element:img,imgAttributes,onCanvas});
|
||||
return _getSVGNative({filenameParts,theme,cacheReady,containerElement: img,file,exportSettings,loader});
|
||||
return await _getSVGNative({filenameParts,theme,cacheReady,containerElement: img,file,exportSettings,loader});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addSVGToImgSrc = (img: HTMLImageElement, svg: SVGSVGElement, cacheReady: boolean, cacheKey: ImageKey):HTMLImageElement => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(addSVGToImgSrc, `MarkdownPostProcessor.ts > addSVGToImgSrc`);
|
||||
const svgString = new XMLSerializer().serializeToString(svg);
|
||||
const blob = new Blob([svgString], { type: 'image/svg+xml' });
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
@@ -375,6 +386,7 @@ const createImgElement = async (
|
||||
attr: imgElementAttributes,
|
||||
onCanvas: boolean = false,
|
||||
) :Promise<HTMLElement> => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(createImgElement, `MarkdownPostProcessor.ts > createImgElement`);
|
||||
const imgOrDiv = await getIMG(attr,onCanvas);
|
||||
if(!imgOrDiv) {
|
||||
return null;
|
||||
@@ -502,6 +514,7 @@ const createImageDiv = async (
|
||||
attr: imgElementAttributes,
|
||||
onCanvas: boolean = false
|
||||
): Promise<HTMLDivElement> => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(createImageDiv, `MarkdownPostProcessor.ts > createImageDiv`);
|
||||
const img = await createImgElement(attr, onCanvas);
|
||||
return createDiv(attr.style.join(" "), (el) => el.append(img));
|
||||
};
|
||||
@@ -510,6 +523,7 @@ const processReadingMode = async (
|
||||
embeddedItems: NodeListOf<Element> | [HTMLElement],
|
||||
ctx: MarkdownPostProcessorContext,
|
||||
) => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING_MPP && debug(processReadingMode, `MarkdownPostProcessor.ts > processReadingMode`);
|
||||
//We are processing a non-excalidraw file in reading mode
|
||||
//Embedded files will be displayed in an .internal-embed container
|
||||
|
||||
@@ -541,6 +555,7 @@ const processReadingMode = async (
|
||||
};
|
||||
|
||||
const processInternalEmbed = async (internalEmbedEl: Element, file: TFile ):Promise<HTMLDivElement> => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING_MPP && debug(processInternalEmbed, `MarkdownPostProcessor.ts > processInternalEmbed`, internalEmbedEl);
|
||||
const attr: imgElementAttributes = {
|
||||
fname: "",
|
||||
fheight: "",
|
||||
@@ -577,6 +592,7 @@ const processAltText = (
|
||||
alt:string,
|
||||
attr: imgElementAttributes
|
||||
) => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(processAltText, `MarkdownPostProcessor.ts > processAltText`);
|
||||
if (alt && !alt.startsWith(fname)) {
|
||||
//2:width, 3:height, 4:style 12 3 4
|
||||
const parts = alt.match(/[^\|\d]*\|?((\d*%?)x?(\d*%?))?\|?(.*)/);
|
||||
@@ -596,6 +612,7 @@ const processAltText = (
|
||||
}
|
||||
|
||||
const isTextOnlyEmbed = (internalEmbedEl: Element):boolean => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(isTextOnlyEmbed, `MarkdownPostProcessor.ts > isTextOnlyEmbed`);
|
||||
const src = internalEmbedEl.getAttribute("src");
|
||||
if(!src) return true; //technically this does not mean this is a text only embed, but still should abort further processing
|
||||
const fnameParts = getEmbeddedFilenameParts(src);
|
||||
@@ -607,6 +624,7 @@ const tmpObsidianWYSIWYG = async (
|
||||
el: HTMLElement,
|
||||
ctx: MarkdownPostProcessorContext,
|
||||
) => {
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING_MPP && debug(tmpObsidianWYSIWYG, `MarkdownPostProcessor.ts > tmpObsidianWYSIWYG`);
|
||||
const file = app.vault.getAbstractFileByPath(ctx.sourcePath);
|
||||
if(!(file instanceof TFile)) return;
|
||||
if(!plugin.isExcalidrawFile(file)) return;
|
||||
@@ -762,6 +780,7 @@ const tmpObsidianWYSIWYG = async (
|
||||
});
|
||||
};
|
||||
|
||||
const docIDs = new Set<string>();
|
||||
/**
|
||||
*
|
||||
* @param el
|
||||
@@ -771,12 +790,29 @@ export const markdownPostProcessor = async (
|
||||
el: HTMLElement,
|
||||
ctx: MarkdownPostProcessorContext,
|
||||
) => {
|
||||
|
||||
(process.env.NODE_ENV === 'development') && DEBUGGING_MPP && debug(markdownPostProcessor, `MarkdownPostProcessor.ts > markdownPostProcessor`, ctx, el);
|
||||
//check to see if we are rendering in editing mode or live preview
|
||||
//if yes, then there should be no .internal-embed containers
|
||||
//if yes, then there should be no .internal-embed containers
|
||||
const isPrinting = Boolean(document.body.querySelectorAll("body > .print").length>0);
|
||||
const isPreview = isPrinting ||
|
||||
//@ts-ignore
|
||||
Boolean(ctx.containerEl && getParentOfClass(ctx.containerEl, "markdown-reading-view")) ||
|
||||
//@ts-ignore
|
||||
(Boolean(ctx.containerEl && getParentOfClass(ctx.containerEl, "hover-popover")) &&
|
||||
Boolean(ctx?.frontmatter?.["excalidraw-open-md"])) ;
|
||||
const embeddedItems = el.querySelectorAll(".internal-embed");
|
||||
if (embeddedItems.length === 0) {
|
||||
tmpObsidianWYSIWYG(el, ctx);
|
||||
if (!isPreview && embeddedItems.length === 0) {
|
||||
if(el.hasClass("mod-frontmatter")) {
|
||||
docIDs.add(ctx.docId);
|
||||
} else {
|
||||
if(docIDs.has(ctx.docId)) {
|
||||
if(!el.hasChildNodes()) {
|
||||
docIDs.delete(ctx.docId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
await tmpObsidianWYSIWYG(el, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -785,12 +821,14 @@ export const markdownPostProcessor = async (
|
||||
//transcluded text element or some other transcluded content inside the Excalidraw file
|
||||
//in reading mode these elements should be hidden
|
||||
const excalidrawFile = Boolean(ctx.frontmatter?.hasOwnProperty("excalidraw-plugin"));
|
||||
const isPrinting = Boolean(document.body.querySelectorAll("body > .print"));
|
||||
if (excalidrawFile && !isPrinting) {
|
||||
if (!isPreview && excalidrawFile) {
|
||||
el.style.display = "none";
|
||||
return;
|
||||
}
|
||||
|
||||
if(isPrinting && el.hasClass("mod-frontmatter")) {
|
||||
return;
|
||||
}
|
||||
await processReadingMode(embeddedItems, ctx);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ButtonComponent, TFile } from "obsidian";
|
||||
import { ButtonComponent, TFile, ToggleComponent } from "obsidian";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { getPDFDoc } from "src/utils/FileUtils";
|
||||
@@ -7,9 +7,11 @@ import { FileSuggestionModal } from "./FolderSuggester";
|
||||
import { getEA } from "src";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
import { ExcalidrawImperativeAPI } from "@zsviczian/excalidraw/types/excalidraw/types";
|
||||
import { t } from "src/lang/helpers";
|
||||
|
||||
export class InsertPDFModal extends Modal {
|
||||
private borderBox: boolean = true;
|
||||
private frame: boolean = false;
|
||||
private gapSize:number = 20;
|
||||
private groupPages: boolean = false;
|
||||
private direction: "down" | "right" = "right";
|
||||
@@ -48,6 +50,7 @@ export class InsertPDFModal extends Modal {
|
||||
if(this.dirty) {
|
||||
this.plugin.settings.pdfImportScale = this.importScale;
|
||||
this.plugin.settings.pdfBorderBox = this.borderBox;
|
||||
this.plugin.settings.pdfFrame = this.frame;
|
||||
this.plugin.settings.pdfGapSize = this.gapSize;
|
||||
this.plugin.settings.pdfGroupPages = this.groupPages;
|
||||
this.plugin.settings.pdfNumColumns = this.numColumns;
|
||||
@@ -120,6 +123,7 @@ export class InsertPDFModal extends Modal {
|
||||
async createForm() {
|
||||
await this.plugin.loadSettings();
|
||||
this.borderBox = this.plugin.settings.pdfBorderBox;
|
||||
this.frame = this.plugin.settings.pdfFrame;
|
||||
this.gapSize = this.plugin.settings.pdfGapSize;
|
||||
this.groupPages = this.plugin.settings.pdfGroupPages;
|
||||
this.numColumns = this.plugin.settings.pdfNumColumns;
|
||||
@@ -138,13 +142,13 @@ export class InsertPDFModal extends Modal {
|
||||
|
||||
const importButtonMessages = () => {
|
||||
if(!this.pdfDoc) {
|
||||
importMessage.innerText = "Please select a PDF file";
|
||||
importMessage.innerText = t("IPM_SELECT_PDF");
|
||||
importButton.buttonEl.style.display="none";
|
||||
return;
|
||||
}
|
||||
if(this.pagesToImport.length === 0) {
|
||||
importButton.buttonEl.style.display="none";
|
||||
importMessage.innerText = "Please select pages to import";
|
||||
importMessage.innerText = t("IPM_SELECT_PAGES_TO_IMPORT");
|
||||
return
|
||||
}
|
||||
if(Math.max(...this.pagesToImport) <= this.pdfDoc.numPages) {
|
||||
@@ -161,7 +165,7 @@ export class InsertPDFModal extends Modal {
|
||||
|
||||
const numPagesMessages = () => {
|
||||
if(numPages === 0) {
|
||||
numPagesMessage.innerText = "Please select a PDF file";
|
||||
numPagesMessage.innerText = t("IPM_SELECT_PDF");
|
||||
return;
|
||||
}
|
||||
numPagesMessage.innerHTML = `There are <b>${numPages}</b> pages in the selected document.`;
|
||||
@@ -211,7 +215,7 @@ export class InsertPDFModal extends Modal {
|
||||
numPagesMessage = ce.createEl("p", {text: ""});
|
||||
numPagesMessages();
|
||||
new Setting(ce)
|
||||
.setName("Pages to import")
|
||||
.setName(t("IPM_PAGES_TO_IMPORT_NAME"))
|
||||
.setDesc("e.g.: 1,3-5,7,9-10")
|
||||
.addText(text => {
|
||||
pageRangesTextComponent = text;
|
||||
@@ -222,18 +226,52 @@ export class InsertPDFModal extends Modal {
|
||||
})
|
||||
importPagesMessage = ce.createEl("p", {text: ""});
|
||||
|
||||
new Setting(ce)
|
||||
.setName("Add border box")
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.borderBox)
|
||||
.onChange((value) => {
|
||||
this.borderBox = value;
|
||||
this.dirty = true;
|
||||
}))
|
||||
let bbToggle: ToggleComponent;
|
||||
let fToggle: ToggleComponent;
|
||||
let laiToggle: ToggleComponent;
|
||||
|
||||
this.frame = this.borderBox ? false : this.frame;
|
||||
|
||||
new Setting(ce)
|
||||
.setName("Group pages")
|
||||
.setDesc("This will group all pages into a single group. This is recommended if you are locking the pages after import, because the group will be easier to unlock later rather than unlocking one by one.")
|
||||
.setName(t("IPM_ADD_BORDER_BOX_NAME"))
|
||||
.addToggle(toggle => {
|
||||
bbToggle = toggle;
|
||||
toggle
|
||||
.setValue(this.borderBox)
|
||||
.onChange((value) => {
|
||||
this.borderBox = value;
|
||||
if(value) {
|
||||
this.frame = false;
|
||||
fToggle.setValue(false);
|
||||
}
|
||||
this.dirty = true;
|
||||
})
|
||||
})
|
||||
|
||||
new Setting(ce)
|
||||
.setName(t("IPM_ADD_FRAME_NAME"))
|
||||
.setDesc(t("IPM_ADD_FRAME_DESC"))
|
||||
.addToggle(toggle => {
|
||||
fToggle = toggle;
|
||||
toggle
|
||||
.setValue(this.frame)
|
||||
.onChange((value) => {
|
||||
this.frame = value;
|
||||
if(value) {
|
||||
this.borderBox = false;
|
||||
bbToggle.setValue(false);
|
||||
if(!this.lockAfterImport) {
|
||||
this.lockAfterImport = true;
|
||||
laiToggle.setValue(true);
|
||||
}
|
||||
}
|
||||
this.dirty = true;
|
||||
})
|
||||
})
|
||||
|
||||
new Setting(ce)
|
||||
.setName(t("IPM_GROUP_PAGES_NAME"))
|
||||
.setDesc(t("IPM_GROUP_PAGES_DESC"))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.groupPages)
|
||||
.onChange((value) => {
|
||||
@@ -244,12 +282,15 @@ export class InsertPDFModal extends Modal {
|
||||
|
||||
new Setting(ce)
|
||||
.setName("Lock pages on canvas after import")
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.lockAfterImport)
|
||||
.onChange((value) => {
|
||||
this.lockAfterImport = value
|
||||
this.dirty = true;
|
||||
}))
|
||||
.addToggle(toggle => {
|
||||
laiToggle = toggle;
|
||||
toggle
|
||||
.setValue(this.lockAfterImport)
|
||||
.onChange((value) => {
|
||||
this.lockAfterImport = value
|
||||
this.dirty = true;
|
||||
})
|
||||
})
|
||||
|
||||
let numColumnsSetting: Setting;
|
||||
let numRowsSetting: Setting;
|
||||
@@ -391,6 +432,12 @@ export class InsertPDFModal extends Modal {
|
||||
if(this.lockAfterImport) imgEl.locked = true;
|
||||
|
||||
ea.addToGroup([boxID,imageID]);
|
||||
|
||||
if(this.frame) {
|
||||
const frameID = ea.addFrame(topX, topY,imgWidth,imgHeight,`${page}`);
|
||||
ea.addElementsToFrame(frameID, [boxID,imageID]);
|
||||
ea.getElement(frameID).link = this.pdfFile.path + `#page=${page}`;
|
||||
}
|
||||
|
||||
switch(this.direction) {
|
||||
case "right":
|
||||
@@ -404,7 +451,9 @@ export class InsertPDFModal extends Modal {
|
||||
}
|
||||
}
|
||||
if(this.groupPages) {
|
||||
const ids = ea.getElements().map(el => el.id);
|
||||
const ids = ea.getElements()
|
||||
.filter(el=>!this.frame || (el.type === "frame"))
|
||||
.map(el => el.id);
|
||||
ea.addToGroup(ids);
|
||||
}
|
||||
await ea.addElementsToView(true,true,false);
|
||||
|
||||
@@ -233,6 +233,18 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
desc: null,
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "addElementsToFrame",
|
||||
code: "addElementsToFrame(frameId: string, elementIDs: string[]):void;",
|
||||
desc: null,
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "addFrame",
|
||||
code: "addFrame(topX: number, topY: number, width: number, height: number, name?: string): string;",
|
||||
desc: null,
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "addRect",
|
||||
code: "addRect(topX: number, topY: number, width: number, height: number, id?:string): string;",
|
||||
@@ -311,6 +323,12 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
desc: "This is an async function, you need to avait the results. Adds a LaTex element to the drawing. The tex string is the LaTex code. The function returns the id of the created element.",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "tex2dataURL",
|
||||
code: "async tex2dataURL(tex: string, scale: number = 4): Promise<{mimeType: MimeType;fileId: FileId;dataURL: DataURL;created: number;size: { height: number; width: number };}> ",
|
||||
desc: "returns the base64 dataURL of the LaTeX equation rendered as an SVG. tex is the LaTeX equation string",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
field: "connectObjects",
|
||||
code: "connectObjects(objectA: string, connectionA: ConnectionPoint, objectB: string, connectionB: ConnectionPoint, formatting?: {numberOfPoints?: number; startArrowHead?: string; endArrowHead?: string; padding?: number;},): string;",
|
||||
@@ -387,8 +405,8 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
},
|
||||
{
|
||||
field: "getViewSelectedElements",
|
||||
code: "getViewSelectedElements(): ExcalidrawElement[];",
|
||||
desc: null,
|
||||
code: "getViewSelectedElements(includeFrameChildren: boolean = true): ExcalidrawElement[];",
|
||||
desc: "If a frame is selected this function will return the frame and all its elements unless includeFrameChildren is set to false",
|
||||
after: "",
|
||||
},
|
||||
{
|
||||
@@ -489,8 +507,9 @@ export const EXCALIDRAW_AUTOMATE_INFO: SuggesterInfo[] = [
|
||||
},
|
||||
{
|
||||
field: "getElementsInTheSameGroupWithElement",
|
||||
code: "getElementsInTheSameGroupWithElement(element: ExcalidrawElement, elements: ExcalidrawElement[]): ExcalidrawElement[];",
|
||||
desc: "Gets all the elements from elements[] that share one or more groupIds with element.",
|
||||
code: "getElementsInTheSameGroupWithElement(element: ExcalidrawElement, elements: ExcalidrawElement[], includeFrameElements: boolean = false): ExcalidrawElement[];",
|
||||
desc: "Gets all the elements from elements[] that share one or more groupIds with element.<br>" +
|
||||
"If includeFrameElements is true, then if the frame is part of the group all the elements that are in the frame will also be included in the result set",
|
||||
after: ""
|
||||
},
|
||||
{
|
||||
|
||||
@@ -326,20 +326,24 @@ FILENAME_HEAD: "Filename",
|
||||
SHOW_PEN_MODE_FREEDRAW_CROSSHAIR_DESC:
|
||||
"Show crosshair in pen mode when using the freedraw tool. <b><u>Toggle ON:</u></b> SHOW <b><u>Toggle OFF:</u></b> HIDE<br>"+
|
||||
"The effect depends on the device. Crosshair is typically visible on drawing tablets, MS Surface, but not on iOS.",
|
||||
SHOW_DRAWING_OR_MD_IN_HOVER_PREVIEW_NAME: "Render image in hover preview for MD files",
|
||||
SHOW_DRAWING_OR_MD_IN_HOVER_PREVIEW_NAME: "Render Excalidraw file as an image in hover preview...",
|
||||
SHOW_DRAWING_OR_MD_IN_HOVER_PREVIEW_DESC:
|
||||
"This setting effects files that have the <b>excalidraw-open-md: true</b> frontmatter key.",
|
||||
"...even if the file has the <b>excalidraw-open-md: true</b> frontmatter key.<br>" +
|
||||
"When this setting is off and the file is set to open in md by default, the hover preview will show the " +
|
||||
"markdown side of the document.",
|
||||
SHOW_DRAWING_OR_MD_IN_READING_MODE_NAME: "Render image when in markdown reading mode",
|
||||
SHOW_DRAWING_OR_MD_IN_READING_MODE_DESC:
|
||||
"Must close the active excalidraw/markdown file and reopen it for this change to take effect.<br>When you are in markdown reading mode (aka. reading the back side of the drawing), should the Excalidraw drawing be rendered as an image? " +
|
||||
"This setting will not affect the display of the drawing when you are in Excalidraw mode, when you embed the drawing into a markdown document or when rendering hover preview.<br><ul>" +
|
||||
"<li>See other related setting for <b>PDF Export</b> under 'Embedding and Exporting' further below.</li>" +
|
||||
"<li>Be sure to check out the <b>Fade Out setting</b> in the 'Miscellaneous fetures' section.</li></ul>",
|
||||
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_NAME: "Render image when EXPORT TO PDF in markdown mode",
|
||||
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_NAME: "Render Excalidraw as an image when EXPORTING TO PDF",
|
||||
SHOW_DRAWING_OR_MD_IN_EXPORTPDF_DESC:
|
||||
"Must close the active excalidraw/markdown file and reopen for this change to take effect.<br>When you are printing the markdown side of the note to PDF (aka. the back side of the drawing), should the Excalidraw drawing be rendered as an image?<br><ul>" +
|
||||
"This setting controls the behavior of Excalidraw when exporting a file to PDF in markdown mode using Obsidian's Export to PDF... feature.<br>" +
|
||||
"When <b>enabled</b> the PDF will show the Excalidraw drawing only; when <b>disabled</b> the PDF will show the markdown side of the document.<br><ul>" +
|
||||
"<li>See other related setting for <b>Markdown Reading Mode</b> under 'Appearnace and Behavior' further above.</li>" +
|
||||
"<li>Be sure to check out the <b>Fade Out setting</b> in the 'Miscellaneous fetures' section.</li></ul>",
|
||||
"<li>Be sure to check out the <b>Fade Out setting</b> in the 'Miscellaneous fetures' section.</li></ul><br>" +
|
||||
"⚠️ Note, you must close the active excalidraw/markdown file and reopen for this change to take effect. ⚠️",
|
||||
THEME_HEAD: "Theme and styling",
|
||||
ZOOM_HEAD: "Zoom",
|
||||
DEFAULT_PINCHZOOM_NAME: "Allow pinch zoom in pen mode",
|
||||
@@ -835,4 +839,16 @@ FILENAME_HEAD: "Filename",
|
||||
FRAME_SETTIGNS_NAME: "Display Frame Name",
|
||||
FRAME_SETTINGS_OUTLINE: "Display Frame Outline",
|
||||
FRAME_SETTINGS_CLIP: "Enable Frame Clipping",
|
||||
|
||||
//InsertPDFModal.ts
|
||||
IPM_PAGES_TO_IMPORT_NAME: "Pages to import",
|
||||
IPM_SELECT_PAGES_TO_IMPORT: "Please select pages to import",
|
||||
IPM_ADD_BORDER_BOX_NAME: "Add border box",
|
||||
IPM_ADD_FRAME_NAME: "Add page to frame",
|
||||
IPM_ADD_FRAME_DESC: "For easier handling I recommend to lock the page inside the frame. " +
|
||||
"If, however, you do lock the page inside the frame then the only way to unlock it is to right-click the frame, select remove elements from frame, then unlock the page.",
|
||||
IPM_GROUP_PAGES_NAME: "Group pages",
|
||||
IPM_GROUP_PAGES_DESC: "This will group all pages into a single group. This is recommended if you are locking the pages after import, because the group will be easier to unlock later rather than unlocking one by one.",
|
||||
IPM_SELECT_PDF: "Please select a PDF file",
|
||||
|
||||
};
|
||||
|
||||
@@ -168,6 +168,7 @@ export interface ExcalidrawSettings {
|
||||
numberOfCustomPens: number;
|
||||
pdfScale: number;
|
||||
pdfBorderBox: boolean;
|
||||
pdfFrame: boolean;
|
||||
pdfGapSize: number;
|
||||
pdfGroupPages: boolean;
|
||||
pdfLockAfterImport: boolean;
|
||||
@@ -339,6 +340,7 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = {
|
||||
numberOfCustomPens: 0,
|
||||
pdfScale: 4,
|
||||
pdfBorderBox: true,
|
||||
pdfFrame: false,
|
||||
pdfGapSize: 20,
|
||||
pdfGroupPages: false,
|
||||
pdfLockAfterImport: true,
|
||||
|
||||
@@ -10,7 +10,8 @@ export let DEBUGGING = false;
|
||||
|
||||
export const log = console.log.bind(window.console);
|
||||
export const debug = (fn: Function, fnName: string, ...messages: unknown[]) => {
|
||||
console.log(fnName,fn,...messages);
|
||||
//console.log(fnName,fn,...messages);
|
||||
console.log(fnName, ...messages);
|
||||
};
|
||||
|
||||
export class CustomMutationObserver {
|
||||
|
||||
@@ -253,16 +253,16 @@ class ImageCache {
|
||||
});
|
||||
}
|
||||
|
||||
private async getObjectStore(mode: IDBTransactionMode, storeName: string): Promise<IDBObjectStore> {
|
||||
private getObjectStore(mode: IDBTransactionMode, storeName: string): IDBObjectStore {
|
||||
const transaction = this.db!.transaction(storeName, mode);
|
||||
return transaction.objectStore(storeName);
|
||||
}
|
||||
|
||||
private async getCacheData(key: string): Promise<FileCacheData | null> {
|
||||
const store = await this.getObjectStore("readonly", this.cacheStoreName);
|
||||
const store = this.getObjectStore("readonly", this.cacheStoreName);
|
||||
const request = store.get(key);
|
||||
|
||||
return new Promise<FileCacheData | null>((resolve, reject) => {
|
||||
return await new Promise<FileCacheData | null>((resolve, reject) => {
|
||||
request.onsuccess = () => {
|
||||
const result = request.result as FileCacheData;
|
||||
resolve(result || null);
|
||||
@@ -275,7 +275,7 @@ class ImageCache {
|
||||
}
|
||||
|
||||
private async getBackupData(key: BackupKey): Promise<BackupData | null> {
|
||||
const store = await this.getObjectStore("readonly", this.backupStoreName);
|
||||
const store = this.getObjectStore("readonly", this.backupStoreName);
|
||||
const request = store.get(key);
|
||||
|
||||
return new Promise<BackupData | null>((resolve, reject) => {
|
||||
@@ -308,7 +308,9 @@ class ImageCache {
|
||||
? await this.getCacheData(key)
|
||||
: await Promise.race([
|
||||
this.getCacheData(key),
|
||||
new Promise<undefined>((_,reject) => setTimeout(() => reject(undefined), 100))
|
||||
new Promise<undefined>((_,reject) => setTimeout(() => {
|
||||
reject(undefined);
|
||||
}, 100))
|
||||
]);
|
||||
this.fullyInitialized = true;
|
||||
if(!cachedData) return undefined;
|
||||
|
||||
Reference in New Issue
Block a user