mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
Compare commits
12 Commits
2.6.3-beta
...
fix-textwr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb83523c0f | ||
|
|
f83c0a8458 | ||
|
|
7411d51477 | ||
|
|
55ce6456d8 | ||
|
|
da6619d55e | ||
|
|
6033c057c2 | ||
|
|
0efda1d6a6 | ||
|
|
59107f0c2a | ||
|
|
f7cd05f6c4 | ||
|
|
5cbd98e543 | ||
|
|
e2d5966ca3 | ||
|
|
dec2909db0 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-excalidraw-plugin",
|
"id": "obsidian-excalidraw-plugin",
|
||||||
"name": "Excalidraw",
|
"name": "Excalidraw",
|
||||||
"version": "2.6.3-beta-1",
|
"version": "2.6.4",
|
||||||
"minAppVersion": "1.1.6",
|
"minAppVersion": "1.1.6",
|
||||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||||
"author": "Zsolt Viczian",
|
"author": "Zsolt Viczian",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-excalidraw-plugin",
|
"id": "obsidian-excalidraw-plugin",
|
||||||
"name": "Excalidraw",
|
"name": "Excalidraw",
|
||||||
"version": "2.6.2",
|
"version": "2.6.4",
|
||||||
"minAppVersion": "1.1.6",
|
"minAppVersion": "1.1.6",
|
||||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||||
"author": "Zsolt Viczian",
|
"author": "Zsolt Viczian",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"@zsviczian/excalidraw": "0.17.6-9",
|
"@zsviczian/excalidraw": "0.17.6-11",
|
||||||
"chroma-js": "^2.4.2",
|
"chroma-js": "^2.4.2",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
"@zsviczian/colormaster": "^1.2.2",
|
"@zsviczian/colormaster": "^1.2.2",
|
||||||
@@ -38,6 +38,7 @@
|
|||||||
"es6-promise-pool": "2.5.0"
|
"es6-promise-pool": "2.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"jsesc": "^3.0.2",
|
||||||
"@babel/core": "^7.22.9",
|
"@babel/core": "^7.22.9",
|
||||||
"@babel/preset-env": "^7.22.10",
|
"@babel/preset-env": "^7.22.10",
|
||||||
"@babel/preset-react": "^7.22.5",
|
"@babel/preset-react": "^7.22.5",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Extension } from "@codemirror/state";
|
import { Extension } from "@codemirror/state";
|
||||||
import ExcalidrawPlugin from "src/main";
|
import ExcalidrawPlugin from "src/main";
|
||||||
import { HideTextBetweenCommentsExtension } from "./Fadeout";
|
import { HideTextBetweenCommentsExtension } from "./Fadeout";
|
||||||
|
import { debug, DEBUGGING } from "src/utils/DebugHelper";
|
||||||
export const EDITOR_FADEOUT = "fadeOutExcalidrawMarkup";
|
export const EDITOR_FADEOUT = "fadeOutExcalidrawMarkup";
|
||||||
|
|
||||||
const editorExtensions: {[key:string]:Extension}= {
|
const editorExtensions: {[key:string]:Extension}= {
|
||||||
@@ -10,13 +11,16 @@ const editorExtensions: {[key:string]:Extension}= {
|
|||||||
export class EditorHandler {
|
export class EditorHandler {
|
||||||
private activeEditorExtensions: Extension[] = [];
|
private activeEditorExtensions: Extension[] = [];
|
||||||
|
|
||||||
constructor(private plugin: ExcalidrawPlugin) {}
|
constructor(private plugin: ExcalidrawPlugin) {
|
||||||
|
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(EditorHandler, `ExcalidrawPlugin.construct EditorHandler`);
|
||||||
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
this.plugin = null;
|
this.plugin = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
setup(): void {
|
setup(): void {
|
||||||
|
(process.env.NODE_ENV === 'development') && DEBUGGING && debug(this.setup, `ExcalidrawPlugin.construct EditorHandler.setup`);
|
||||||
this.plugin.registerEditorExtension(this.activeEditorExtensions);
|
this.plugin.registerEditorExtension(this.activeEditorExtensions);
|
||||||
this.updateCMExtensionState(EDITOR_FADEOUT, this.plugin.settings.fadeOutExcalidrawMarkup);
|
this.updateCMExtensionState(EDITOR_FADEOUT, this.plugin.settings.fadeOutExcalidrawMarkup);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1108,66 +1108,8 @@ export class EmbeddedFilesLoader {
|
|||||||
const getSVGData = async (app: App, file: TFile, colorMap: ColorMap | null): Promise<DataURL> => {
|
const getSVGData = async (app: App, file: TFile, colorMap: ColorMap | null): Promise<DataURL> => {
|
||||||
const svgString = replaceSVGColors(await app.vault.read(file), colorMap) as string;
|
const svgString = replaceSVGColors(await app.vault.read(file), colorMap) as string;
|
||||||
return svgToBase64(svgString) as DataURL;
|
return svgToBase64(svgString) as DataURL;
|
||||||
/*
|
|
||||||
try {
|
|
||||||
const container = document.createElement('div');
|
|
||||||
container.innerHTML = svgString;
|
|
||||||
|
|
||||||
const svgElement = container.querySelector('svg');
|
|
||||||
|
|
||||||
if (!svgElement) {
|
|
||||||
throw new Error('Invalid SVG content'); // Ensure there's an SVG element
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for width and height attributes
|
|
||||||
const hasWidth = svgElement.hasAttribute('width');
|
|
||||||
const hasHeight = svgElement.hasAttribute('height');
|
|
||||||
|
|
||||||
// If width or height is missing, calculate based on viewBox
|
|
||||||
if (!hasWidth || !hasHeight) {
|
|
||||||
const viewBox = svgElement.getAttribute('viewBox');
|
|
||||||
|
|
||||||
if (viewBox) {
|
|
||||||
const [ , , viewBoxWidth, viewBoxHeight] = viewBox.split(/\s+/).map(Number);
|
|
||||||
|
|
||||||
// Set width and height based on viewBox if they are missing
|
|
||||||
if (!hasWidth) {
|
|
||||||
svgElement.setAttribute('width', `${viewBoxWidth}px`);
|
|
||||||
}
|
|
||||||
if (!hasHeight) {
|
|
||||||
svgElement.setAttribute('height', `${viewBoxHeight}px`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the updated SVG string from outerHTML
|
|
||||||
const updatedSVGString = svgElement.outerHTML;
|
|
||||||
|
|
||||||
// Convert the updated SVG string to a base64 Data URL
|
|
||||||
return svgToBase64(updatedSVGString) as DataURL;
|
|
||||||
} catch (error) {
|
|
||||||
errorlog({ where: "EmbeddedFileLoader.getSVGData", error });
|
|
||||||
return svgToBase64(svgString) as DataURL;
|
|
||||||
}*/
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*export const generateIdFromFile = async (file: ArrayBuffer): Promise<FileId> => {
|
|
||||||
let id: FileId;
|
|
||||||
try {
|
|
||||||
const hashBuffer = await window.crypto.subtle.digest("SHA-1", file);
|
|
||||||
id =
|
|
||||||
// convert buffer to byte array
|
|
||||||
Array.from(new Uint8Array(hashBuffer))
|
|
||||||
// convert to hex string
|
|
||||||
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
||||||
.join("") as FileId;
|
|
||||||
} catch (error) {
|
|
||||||
errorlog({ where: "EmbeddedFileLoader.generateIdFromFile", error });
|
|
||||||
id = fileid() as FileId;
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
};*/
|
|
||||||
|
|
||||||
export const generateIdFromFile = async (file: ArrayBuffer, key?: string): Promise<FileId> => {
|
export const generateIdFromFile = async (file: ArrayBuffer, key?: string): Promise<FileId> => {
|
||||||
let id: FileId;
|
let id: FileId;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1547,6 +1547,10 @@ export class ExcalidrawAutomate {
|
|||||||
: imageFile.path + (scale || !anchor ? "":"|100%"),
|
: imageFile.path + (scale || !anchor ? "":"|100%"),
|
||||||
hasSVGwithBitmap: image.hasSVGwithBitmap,
|
hasSVGwithBitmap: image.hasSVGwithBitmap,
|
||||||
latex: null,
|
latex: null,
|
||||||
|
size: { //must have the natural size here (e.g. for PDF cropping)
|
||||||
|
height: image.size.height,
|
||||||
|
width: image.size.width,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
if (scale && (Math.max(image.size.width, image.size.height) > MAX_IMAGE_SIZE)) {
|
if (scale && (Math.max(image.size.width, image.size.height) > MAX_IMAGE_SIZE)) {
|
||||||
const scale =
|
const scale =
|
||||||
@@ -2850,10 +2854,9 @@ export class ExcalidrawAutomate {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function initExcalidrawAutomate(
|
export function initExcalidrawAutomate(
|
||||||
plugin: ExcalidrawPlugin,
|
plugin: ExcalidrawPlugin,
|
||||||
): Promise<ExcalidrawAutomate> {
|
): ExcalidrawAutomate {
|
||||||
await initFonts();
|
|
||||||
const ea = new ExcalidrawAutomate(plugin);
|
const ea = new ExcalidrawAutomate(plugin);
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
window.ExcalidrawAutomate = ea;
|
window.ExcalidrawAutomate = ea;
|
||||||
@@ -2888,14 +2891,6 @@ function getFontFamily(id: number):string {
|
|||||||
return getFontFamilyString({fontFamily:id})
|
return getFontFamilyString({fontFamily:id})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initFonts():Promise<void> {
|
|
||||||
/*await excalidrawLib.registerFontsInCSS();
|
|
||||||
const fonts = excalidrawLib.getFontFamilies();
|
|
||||||
for(let i=0;i<fonts.length;i++) {
|
|
||||||
if(fonts[i] !== "Local Font") await (document as any).fonts.load(`16px ${fonts[i]}`);
|
|
||||||
};*/
|
|
||||||
}
|
|
||||||
|
|
||||||
export function _measureText(
|
export function _measureText(
|
||||||
newText: string,
|
newText: string,
|
||||||
fontSize: number,
|
fontSize: number,
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import { updateElementIdsInScene } from "./utils/ExcalidrawSceneUtils";
|
|||||||
import { getNewUniqueFilepath } from "./utils/FileUtils";
|
import { getNewUniqueFilepath } from "./utils/FileUtils";
|
||||||
import { t } from "./lang/helpers";
|
import { t } from "./lang/helpers";
|
||||||
import { displayFontMessage } from "./utils/ExcalidrawViewUtils";
|
import { displayFontMessage } from "./utils/ExcalidrawViewUtils";
|
||||||
|
import { getPDFRect } from "./utils/PDFUtils";
|
||||||
|
|
||||||
type SceneDataWithFiles = SceneData & { files: BinaryFiles };
|
type SceneDataWithFiles = SceneData & { files: BinaryFiles };
|
||||||
|
|
||||||
@@ -1579,6 +1580,26 @@ export class ExcalidrawData {
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private syncCroppedPDFs() {
|
||||||
|
let dirty = false;
|
||||||
|
const scene = this.scene as SceneDataWithFiles;
|
||||||
|
const pdfScale = this.plugin.settings.pdfScale;
|
||||||
|
scene.elements
|
||||||
|
.filter(el=>el.type === "image" && el.crop && !el.isDeleted)
|
||||||
|
.forEach((el: Mutable<ExcalidrawImageElement>)=>{
|
||||||
|
const ef = this.getFile(el.fileId);
|
||||||
|
if(!ef.file) return;
|
||||||
|
if(ef.file.extension !== "pdf") return;
|
||||||
|
const pageRef = ef.linkParts.original.split("#")?.[1];
|
||||||
|
if(!pageRef || !pageRef.startsWith("page=") || pageRef.includes("rect")) return;
|
||||||
|
const restOfLink = el.link ? el.link.match(/&rect=\d*,\d*,\d*,\d*(.*)/)?.[1] : "";
|
||||||
|
const link = ef.linkParts.original + getPDFRect(el.crop, pdfScale) + (restOfLink ? restOfLink : "]]");
|
||||||
|
el.link = `[[${link}`;
|
||||||
|
this.elementLinks.set(el.id, el.link);
|
||||||
|
dirty = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* deletes fileIds from Excalidraw data for files no longer in the scene
|
* deletes fileIds from Excalidraw data for files no longer in the scene
|
||||||
* @returns
|
* @returns
|
||||||
@@ -1699,6 +1720,7 @@ export class ExcalidrawData {
|
|||||||
this.updateElementLinksFromScene();
|
this.updateElementLinksFromScene();
|
||||||
result =
|
result =
|
||||||
result ||
|
result ||
|
||||||
|
this.syncCroppedPDFs() ||
|
||||||
this.setLinkPrefix() ||
|
this.setLinkPrefix() ||
|
||||||
this.setUrlPrefix() ||
|
this.setUrlPrefix() ||
|
||||||
this.setShowLinkBrackets() ||
|
this.setShowLinkBrackets() ||
|
||||||
|
|||||||
@@ -148,6 +148,7 @@ import { Packages } from "./types/types";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { diagramToHTML } from "./utils/matic";
|
import { diagramToHTML } from "./utils/matic";
|
||||||
import { IS_WORKER_SUPPORTED } from "./workers/compression-worker";
|
import { IS_WORKER_SUPPORTED } from "./workers/compression-worker";
|
||||||
|
import { getPDFCropRect } from "./utils/PDFUtils";
|
||||||
|
|
||||||
const EMBEDDABLE_SEMAPHORE_TIMEOUT = 2000;
|
const EMBEDDABLE_SEMAPHORE_TIMEOUT = 2000;
|
||||||
const PREVENT_RELOAD_TIMEOUT = 2000;
|
const PREVENT_RELOAD_TIMEOUT = 2000;
|
||||||
@@ -218,6 +219,27 @@ export const addFiles = async (
|
|||||||
if (isDark === undefined) {
|
if (isDark === undefined) {
|
||||||
isDark = s.scene.appState.theme;
|
isDark = s.scene.appState.theme;
|
||||||
}
|
}
|
||||||
|
// update element.crop naturalWidth and naturalHeight in case scale of PDF loading has changed
|
||||||
|
// update crop.x crop.y, crop.width, crop.height according to the new scale
|
||||||
|
files
|
||||||
|
.filter((f:FileData) => view.excalidrawData.getFile(f.id)?.file?.extension === "pdf")
|
||||||
|
.forEach((f:FileData) => {
|
||||||
|
s.scene.elements
|
||||||
|
.filter((el:ExcalidrawElement)=>el.type === "image" && el.fileId === f.id && el.crop && el.crop.naturalWidth !== f.size.width)
|
||||||
|
.forEach((el:Mutable<ExcalidrawImageElement>) => {
|
||||||
|
s.dirty = true;
|
||||||
|
const scale = f.size.width / el.crop.naturalWidth;
|
||||||
|
el.crop = {
|
||||||
|
x: el.crop.x * scale,
|
||||||
|
y: el.crop.y * scale,
|
||||||
|
width: el.crop.width * scale,
|
||||||
|
height: el.crop.height * scale,
|
||||||
|
naturalWidth: f.size.width,
|
||||||
|
naturalHeight: f.size.height,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (s.dirty) {
|
if (s.dirty) {
|
||||||
//debug({where:"ExcalidrawView.addFiles",file:view.file.name,dataTheme:view.excalidrawData.scene.appState.theme,before:"updateScene",state:scene.appState})
|
//debug({where:"ExcalidrawView.addFiles",file:view.file.name,dataTheme:view.excalidrawData.scene.appState.theme,before:"updateScene",state:scene.appState})
|
||||||
view.updateScene({
|
view.updateScene({
|
||||||
@@ -4036,7 +4058,20 @@ export default class ExcalidrawView extends TextFileView implements HoverParent{
|
|||||||
} else {
|
} else {
|
||||||
if(link.match(/^[^#]*#page=\d*(&\w*=[^&]+){0,}&rect=\d*,\d*,\d*,\d*/g)) {
|
if(link.match(/^[^#]*#page=\d*(&\w*=[^&]+){0,}&rect=\d*,\d*,\d*,\d*/g)) {
|
||||||
const ea = getEA(this) as ExcalidrawAutomate;
|
const ea = getEA(this) as ExcalidrawAutomate;
|
||||||
await ea.addImage(this.currentPosition.x, this.currentPosition.y,link);
|
const imgID = await ea.addImage(this.currentPosition.x, this.currentPosition.y,link.split("&rect=")[0]);
|
||||||
|
const el = ea.getElement(imgID) as Mutable<ExcalidrawImageElement>;
|
||||||
|
const fd = ea.imagesDict[el.fileId] as FileData;
|
||||||
|
el.crop = getPDFCropRect({
|
||||||
|
scale: this.plugin.settings.pdfScale,
|
||||||
|
link,
|
||||||
|
naturalHeight: fd.size.height,
|
||||||
|
naturalWidth: fd.size.width,
|
||||||
|
});
|
||||||
|
if(el.crop) {
|
||||||
|
el.width = el.crop.width/this.plugin.settings.pdfScale;
|
||||||
|
el.height = el.crop.height/this.plugin.settings.pdfScale;
|
||||||
|
}
|
||||||
|
el.link = `[[${link}]]`;
|
||||||
ea.addElementsToView(false,false).then(()=>ea.destroy());
|
ea.addElementsToView(false,false).then(()=>ea.destroy());
|
||||||
} else {
|
} else {
|
||||||
const modal = new UniversalInsertFileModal(this.plugin, this);
|
const modal = new UniversalInsertFileModal(this.plugin, this);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
App,
|
App,
|
||||||
Instruction,
|
Instruction,
|
||||||
|
normalizePath,
|
||||||
TAbstractFile,
|
TAbstractFile,
|
||||||
TFile,
|
TFile,
|
||||||
WorkspaceLeaf,
|
WorkspaceLeaf,
|
||||||
@@ -22,6 +23,7 @@ export type ScriptIconMap = {
|
|||||||
|
|
||||||
export class ScriptEngine {
|
export class ScriptEngine {
|
||||||
private plugin: ExcalidrawPlugin;
|
private plugin: ExcalidrawPlugin;
|
||||||
|
private app: App;
|
||||||
private scriptPath: string;
|
private scriptPath: string;
|
||||||
//https://stackoverflow.com/questions/60218638/how-to-force-re-render-if-map-value-changes
|
//https://stackoverflow.com/questions/60218638/how-to-force-re-render-if-map-value-changes
|
||||||
public scriptIconMap: ScriptIconMap;
|
public scriptIconMap: ScriptIconMap;
|
||||||
@@ -29,6 +31,7 @@ export class ScriptEngine {
|
|||||||
|
|
||||||
constructor(plugin: ExcalidrawPlugin) {
|
constructor(plugin: ExcalidrawPlugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
this.app = plugin.app;
|
||||||
this.scriptIconMap = {};
|
this.scriptIconMap = {};
|
||||||
this.loadScripts();
|
this.loadScripts();
|
||||||
this.registerEventHandlers();
|
this.registerEventHandlers();
|
||||||
@@ -58,7 +61,7 @@ export class ScriptEngine {
|
|||||||
if (!path.endsWith(".svg")) {
|
if (!path.endsWith(".svg")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const scriptFile = app.vault.getAbstractFileByPath(
|
const scriptFile = this.app.vault.getAbstractFileByPath(
|
||||||
getIMGFilename(path, "md"),
|
getIMGFilename(path, "md"),
|
||||||
);
|
);
|
||||||
if (scriptFile && scriptFile instanceof TFile) {
|
if (scriptFile && scriptFile instanceof TFile) {
|
||||||
@@ -107,19 +110,19 @@ export class ScriptEngine {
|
|||||||
|
|
||||||
registerEventHandlers() {
|
registerEventHandlers() {
|
||||||
this.plugin.registerEvent(
|
this.plugin.registerEvent(
|
||||||
this.plugin.app.vault.on(
|
this.app.vault.on(
|
||||||
"delete",
|
"delete",
|
||||||
(file: TFile)=>this.deleteEventHandler(file)
|
(file: TFile)=>this.deleteEventHandler(file)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
this.plugin.registerEvent(
|
this.plugin.registerEvent(
|
||||||
this.plugin.app.vault.on(
|
this.app.vault.on(
|
||||||
"create",
|
"create",
|
||||||
(file: TFile)=>this.createEventHandler(file)
|
(file: TFile)=>this.createEventHandler(file)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
this.plugin.registerEvent(
|
this.plugin.registerEvent(
|
||||||
this.plugin.app.vault.on(
|
this.app.vault.on(
|
||||||
"rename",
|
"rename",
|
||||||
(file: TAbstractFile, oldPath: string)=>this.renameEventHandler(file, oldPath)
|
(file: TAbstractFile, oldPath: string)=>this.renameEventHandler(file, oldPath)
|
||||||
),
|
),
|
||||||
@@ -138,15 +141,16 @@ export class ScriptEngine {
|
|||||||
|
|
||||||
public getListofScripts(): TFile[] {
|
public getListofScripts(): TFile[] {
|
||||||
this.scriptPath = this.plugin.settings.scriptFolderPath;
|
this.scriptPath = this.plugin.settings.scriptFolderPath;
|
||||||
if (!app.vault.getAbstractFileByPath(this.scriptPath)) {
|
if(!this.scriptPath) return;
|
||||||
//this.scriptPath = null;
|
this.scriptPath = normalizePath(this.scriptPath);
|
||||||
|
if (!this.app.vault.getAbstractFileByPath(this.scriptPath)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return app.vault
|
return this.app.vault
|
||||||
.getFiles()
|
.getFiles()
|
||||||
.filter(
|
.filter(
|
||||||
(f: TFile) =>
|
(f: TFile) =>
|
||||||
f.path.startsWith(this.scriptPath) && f.extension === "md",
|
f.path.startsWith(this.scriptPath+"/") && f.extension === "md",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +170,10 @@ export class ScriptEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const subpath = path.split(`${this.scriptPath}/`)[1];
|
const subpath = path.split(`${this.scriptPath}/`)[1];
|
||||||
const lastSlash = subpath.lastIndexOf("/");
|
if(!subpath) {
|
||||||
|
console.warn(`ScriptEngine.getScriptName unexpected basename: ${basename}; path: ${path}`)
|
||||||
|
}
|
||||||
|
const lastSlash = subpath?.lastIndexOf("/");
|
||||||
if (lastSlash > -1) {
|
if (lastSlash > -1) {
|
||||||
return subpath.substring(0, lastSlash + 1) + basename;
|
return subpath.substring(0, lastSlash + 1) + basename;
|
||||||
}
|
}
|
||||||
@@ -175,10 +182,10 @@ export class ScriptEngine {
|
|||||||
|
|
||||||
async addScriptIconToMap(scriptPath: string, name: string) {
|
async addScriptIconToMap(scriptPath: string, name: string) {
|
||||||
const svgFilePath = getIMGFilename(scriptPath, "svg");
|
const svgFilePath = getIMGFilename(scriptPath, "svg");
|
||||||
const file = app.vault.getAbstractFileByPath(svgFilePath);
|
const file = this.app.vault.getAbstractFileByPath(svgFilePath);
|
||||||
const svgString: string =
|
const svgString: string =
|
||||||
file && file instanceof TFile
|
file && file instanceof TFile
|
||||||
? await app.vault.read(file)
|
? await this.app.vault.read(file)
|
||||||
: null;
|
: null;
|
||||||
this.scriptIconMap = {
|
this.scriptIconMap = {
|
||||||
...this.scriptIconMap,
|
...this.scriptIconMap,
|
||||||
@@ -199,12 +206,12 @@ export class ScriptEngine {
|
|||||||
name: `(Script) ${scriptName}`,
|
name: `(Script) ${scriptName}`,
|
||||||
checkCallback: (checking: boolean) => {
|
checkCallback: (checking: boolean) => {
|
||||||
if (checking) {
|
if (checking) {
|
||||||
return Boolean(app.workspace.getActiveViewOfType(ExcalidrawView));
|
return Boolean(this.app.workspace.getActiveViewOfType(ExcalidrawView));
|
||||||
}
|
}
|
||||||
const view = app.workspace.getActiveViewOfType(ExcalidrawView);
|
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||||
if (view) {
|
if (view) {
|
||||||
(async()=>{
|
(async()=>{
|
||||||
const script = await app.vault.read(f);
|
const script = await this.app.vault.read(f);
|
||||||
if(script) {
|
if(script) {
|
||||||
//remove YAML frontmatter if present
|
//remove YAML frontmatter if present
|
||||||
this.executeScript(view, script, scriptName,f);
|
this.executeScript(view, script, scriptName,f);
|
||||||
@@ -218,7 +225,7 @@ export class ScriptEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unloadScripts() {
|
unloadScripts() {
|
||||||
const scripts = app.vault
|
const scripts = this.app.vault
|
||||||
.getFiles()
|
.getFiles()
|
||||||
.filter((f: TFile) => f.path.startsWith(this.scriptPath));
|
.filter((f: TFile) => f.path.startsWith(this.scriptPath));
|
||||||
scripts.forEach((f) => {
|
scripts.forEach((f) => {
|
||||||
@@ -236,11 +243,11 @@ export class ScriptEngine {
|
|||||||
|
|
||||||
const commandId = `${PLUGIN_ID}:${basename}`;
|
const commandId = `${PLUGIN_ID}:${basename}`;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (!this.plugin.app.commands.commands[commandId]) {
|
if (!this.app.commands.commands[commandId]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
delete this.plugin.app.commands.commands[commandId];
|
delete this.app.commands.commands[commandId];
|
||||||
}
|
}
|
||||||
|
|
||||||
async executeScript(view: ExcalidrawView, script: string, title: string, file: TFile) {
|
async executeScript(view: ExcalidrawView, script: string, title: string, file: TFile) {
|
||||||
@@ -271,7 +278,7 @@ export class ScriptEngine {
|
|||||||
ScriptEngine.inputPrompt(
|
ScriptEngine.inputPrompt(
|
||||||
view,
|
view,
|
||||||
this.plugin,
|
this.plugin,
|
||||||
this.plugin.app,
|
this.app,
|
||||||
header,
|
header,
|
||||||
placeholder,
|
placeholder,
|
||||||
value,
|
value,
|
||||||
@@ -288,7 +295,7 @@ export class ScriptEngine {
|
|||||||
instructions?: Instruction[],
|
instructions?: Instruction[],
|
||||||
) =>
|
) =>
|
||||||
ScriptEngine.suggester(
|
ScriptEngine.suggester(
|
||||||
this.plugin.app,
|
this.app,
|
||||||
displayItems,
|
displayItems,
|
||||||
items,
|
items,
|
||||||
hint,
|
hint,
|
||||||
@@ -304,7 +311,7 @@ export class ScriptEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private updateToolPannels() {
|
private updateToolPannels() {
|
||||||
const excalidrawViews = getExcalidrawViews(this.plugin.app);
|
const excalidrawViews = getExcalidrawViews(this.app);
|
||||||
excalidrawViews.forEach(excalidrawView => {
|
excalidrawViews.forEach(excalidrawView => {
|
||||||
excalidrawView.toolsPanelRef?.current?.updateScriptIconMap(
|
excalidrawView.toolsPanelRef?.current?.updateScriptIconMap(
|
||||||
this.scriptIconMap,
|
this.scriptIconMap,
|
||||||
|
|||||||
@@ -222,6 +222,7 @@ export const FRONTMATTER_KEYS:{[key:string]: {name: string, type: string, depric
|
|||||||
|
|
||||||
export const EMBEDDABLE_THEME_FRONTMATTER_VALUES = ["light", "dark", "auto", "dafault"];
|
export const EMBEDDABLE_THEME_FRONTMATTER_VALUES = ["light", "dark", "auto", "dafault"];
|
||||||
export const VIEW_TYPE_EXCALIDRAW = "excalidraw";
|
export const VIEW_TYPE_EXCALIDRAW = "excalidraw";
|
||||||
|
export const VIEW_TYPE_EXCALIDRAW_LOADING = "excalidraw-loading";
|
||||||
export const ICON_NAME = "excalidraw-icon";
|
export const ICON_NAME = "excalidraw-icon";
|
||||||
export const MAX_COLORS = 5;
|
export const MAX_COLORS = 5;
|
||||||
export const COLOR_FREQ = 6;
|
export const COLOR_FREQ = 6;
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import { FileView, TextFileView, View, WorkspaceLeaf } from "obsidian";
|
import { App, FileView, WorkspaceLeaf } from "obsidian";
|
||||||
|
import { VIEW_TYPE_EXCALIDRAW_LOADING } from "src/constants/constants";
|
||||||
import ExcalidrawPlugin from "src/main";
|
import ExcalidrawPlugin from "src/main";
|
||||||
import { setExcalidrawView } from "src/utils/ObsidianUtils";
|
import { setExcalidrawView } from "src/utils/ObsidianUtils";
|
||||||
|
|
||||||
export default class ExcalidrawLoading extends FileView {
|
export function switchToExcalidraw(app: App) {
|
||||||
|
const leaves = app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW_LOADING).filter(l=>l.view instanceof ExcalidrawLoading);
|
||||||
|
leaves.forEach(l=>(l.view as ExcalidrawLoading).switchToeExcalidraw());
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExcalidrawLoading extends FileView {
|
||||||
constructor(leaf: WorkspaceLeaf, private plugin: ExcalidrawPlugin) {
|
constructor(leaf: WorkspaceLeaf, private plugin: ExcalidrawPlugin) {
|
||||||
super(leaf);
|
super(leaf);
|
||||||
this.switchToeExcalidraw();
|
|
||||||
this.displayLoadingText();
|
this.displayLoadingText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,13 +19,12 @@ export default class ExcalidrawLoading extends FileView {
|
|||||||
this.displayLoadingText();
|
this.displayLoadingText();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async switchToeExcalidraw() {
|
public switchToeExcalidraw() {
|
||||||
await this.plugin.awaitInit();
|
|
||||||
setExcalidrawView(this.leaf);
|
setExcalidrawView(this.leaf);
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewType(): string {
|
getViewType(): string {
|
||||||
return "excalidra-loading";
|
return VIEW_TYPE_EXCALIDRAW_LOADING;
|
||||||
}
|
}
|
||||||
|
|
||||||
getDisplayText() {
|
getDisplayText() {
|
||||||
|
|||||||
@@ -17,6 +17,32 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
|
|||||||
|
|
||||||
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://storage.ko-fi.com/cdn/kofi6.png?v=6" border="0" alt="Buy Me a Coffee at ko-fi.com" height=45></a></div>
|
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://storage.ko-fi.com/cdn/kofi6.png?v=6" border="0" alt="Buy Me a Coffee at ko-fi.com" height=45></a></div>
|
||||||
`,
|
`,
|
||||||
|
"2.6.4":`
|
||||||
|
## Fixed
|
||||||
|
- Error saving when cropping images embedded from a URL (not from a file in the Vault) [#2096](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2096)
|
||||||
|
`,
|
||||||
|
"2.6.3":`
|
||||||
|
<div class="excalidraw-videoWrapper"><div>
|
||||||
|
<iframe src="https://www.youtube.com/embed/OfUWAvCgbXk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
|
## New
|
||||||
|
- **Cropping PDF Pages**
|
||||||
|
- Improved PDF++ cropping: You can now double-click cropped images in Excalidraw to adjust the crop area, which will also appear as a highlight in PDF++. This feature applies to PDF cut-outs created in version 2.6.3 and beyond.
|
||||||
|
- **Insert Last Active PDF Page as Image**
|
||||||
|
- New command palette action lets you insert the currently active PDF page into Excalidraw. Ideal for setups with PDF and Excalidraw side-by-side. You can assign a hotkey for quicker access. Cropped areas in Excalidraw will show as highlights in PDF++.
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
- Fixed **Close Settings** button toggle behavior [#2085](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/2085)
|
||||||
|
- Resolved text wrapping issues causing layout shifts due to trailing whitespaces [#8714](https://github.com/excalidraw/excalidraw/pull/8714)
|
||||||
|
- **Aspect Ratio and Size Reset** commands now function correctly with cropped images.
|
||||||
|
- **Cropped Drawings**: Adjustments to cropped Excalidraw drawings are now supported. However, for nested Excalidraw drawings, it's recommended to use area, group, and frame references instead of cropping.
|
||||||
|
|
||||||
|
## Refactoring
|
||||||
|
- Further font loading optimizations on Excalidraw.com; no impact expected in Obsidian [#8693](https://github.com/excalidraw/excalidraw/pull/8693)
|
||||||
|
- Text wrapping improvements [#8715](https://github.com/excalidraw/excalidraw/pull/8715)
|
||||||
|
- Plugin initiation and error handling
|
||||||
|
`,
|
||||||
"2.6.2":`
|
"2.6.2":`
|
||||||
## Fixed
|
## Fixed
|
||||||
- Image scaling issue with SVGs that miss the width and height property. [#8729](https://github.com/excalidraw/excalidraw/issues/8729)
|
- Image scaling issue with SVGs that miss the width and height property. [#8729](https://github.com/excalidraw/excalidraw/issues/8729)
|
||||||
|
|||||||
@@ -713,27 +713,70 @@ export class ConfirmationPrompt extends Modal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function linkPrompt (
|
export async function linkPrompt(
|
||||||
linkText:string,
|
linkText: string,
|
||||||
app: App,
|
app: App,
|
||||||
view?: ExcalidrawView,
|
view?: ExcalidrawView,
|
||||||
message: string = "Select link to open",
|
message: string = t("SELECT_LINK_TO_OPEN"),
|
||||||
):Promise<[file:TFile, linkText:string, subpath: string]> {
|
): Promise<[file: TFile, linkText: string, subpath: string]> {
|
||||||
const linksArray = REGEX_LINK.getResList(linkText);
|
const linksArray = REGEX_LINK.getResList(linkText).filter(x => Boolean(x.value));
|
||||||
const tagsArray = REGEX_TAGS.getResList(linkText.replaceAll(/([^\s])#/g,"$1 "));
|
const links = linksArray.map(x => REGEX_LINK.getLink(x));
|
||||||
|
|
||||||
|
// Create a map to track duplicates by base link (without rect reference)
|
||||||
|
const linkMap = new Map<string, number[]>();
|
||||||
|
links.forEach((link, i) => {
|
||||||
|
const linkBase = link.split("&rect=")[0];
|
||||||
|
if (!linkMap.has(linkBase)) linkMap.set(linkBase, []);
|
||||||
|
linkMap.get(linkBase).push(i);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Determine indices to keep
|
||||||
|
const indicesToKeep = new Set<number>();
|
||||||
|
linkMap.forEach(indices => {
|
||||||
|
if (indices.length === 1) {
|
||||||
|
// Only one link, keep it
|
||||||
|
indicesToKeep.add(indices[0]);
|
||||||
|
} else {
|
||||||
|
// Multiple links: prefer the one with rect reference, if available
|
||||||
|
const rectIndex = indices.find(i => links[i].includes("&rect="));
|
||||||
|
if (rectIndex !== undefined) {
|
||||||
|
indicesToKeep.add(rectIndex);
|
||||||
|
} else {
|
||||||
|
// No rect reference in duplicates, add the first one
|
||||||
|
indicesToKeep.add(indices[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Final validation to ensure each duplicate group has at least one entry
|
||||||
|
linkMap.forEach(indices => {
|
||||||
|
const hasKeptEntry = indices.some(i => indicesToKeep.has(i));
|
||||||
|
if (!hasKeptEntry) {
|
||||||
|
// Add the first index if none were kept
|
||||||
|
indicesToKeep.add(indices[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Filter linksArray, links, itemsDisplay, and items based on indicesToKeep
|
||||||
|
const filteredLinksArray = linksArray.filter((_, i) => indicesToKeep.has(i));
|
||||||
|
const tagsArray = REGEX_TAGS.getResList(linkText.replaceAll(/([^\s])#/g, "$1 ")).filter(x => Boolean(x.value));
|
||||||
|
|
||||||
let subpath: string = null;
|
let subpath: string = null;
|
||||||
let file: TFile = null;
|
let file: TFile = null;
|
||||||
let parts = linksArray[0] ?? tagsArray[0];
|
let parts = filteredLinksArray[0] ?? tagsArray[0];
|
||||||
|
|
||||||
|
// Generate filtered itemsDisplay and items arrays
|
||||||
const itemsDisplay = [
|
const itemsDisplay = [
|
||||||
...linksArray.filter(p=> Boolean(p.value)).map(p => {
|
...filteredLinksArray.map(p => {
|
||||||
const alias = REGEX_LINK.getAliasOrLink(p);
|
const alias = REGEX_LINK.getAliasOrLink(p);
|
||||||
return alias === "100%" ? REGEX_LINK.getLink(p) : alias;
|
return alias === "100%" ? REGEX_LINK.getLink(p) : alias;
|
||||||
}),
|
}),
|
||||||
...tagsArray.filter(x=> Boolean(x.value)).map(x => REGEX_TAGS.getTag(x)),
|
...tagsArray.map(x => REGEX_TAGS.getTag(x)),
|
||||||
];
|
];
|
||||||
|
|
||||||
const items = [
|
const items = [
|
||||||
...linksArray.filter(p=>Boolean(p.value)),
|
...filteredLinksArray,
|
||||||
...tagsArray.filter(x=> Boolean(x.value)),
|
...tagsArray,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (items.length>1) {
|
if (items.length>1) {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { TAG_AUTOEXPORT, TAG_MDREADINGMODE, TAG_PDFEXPORT } from "src/constants/
|
|||||||
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/ModifierkeyHelper";
|
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/ModifierkeyHelper";
|
||||||
|
|
||||||
const CJK_FONTS = "CJK Fonts";
|
const CJK_FONTS = "CJK Fonts";
|
||||||
|
declare const PLUGIN_VERSION:string;
|
||||||
|
|
||||||
// English
|
// English
|
||||||
export default {
|
export default {
|
||||||
// main.ts
|
// main.ts
|
||||||
@@ -76,6 +78,7 @@ export default {
|
|||||||
IMPORT_SVG_CONTEXTMENU: "Convert SVG to strokes - with limitations",
|
IMPORT_SVG_CONTEXTMENU: "Convert SVG to strokes - with limitations",
|
||||||
INSERT_MD: "Insert markdown file from vault",
|
INSERT_MD: "Insert markdown file from vault",
|
||||||
INSERT_PDF: "Insert PDF file from vault",
|
INSERT_PDF: "Insert PDF file from vault",
|
||||||
|
INSERT_LAST_ACTIVE_PDF_PAGE_AS_IMAGE: "Insert last active PDF page as image",
|
||||||
UNIVERSAL_ADD_FILE: "Insert ANY file",
|
UNIVERSAL_ADD_FILE: "Insert ANY file",
|
||||||
INSERT_CARD: "Add back-of-note card",
|
INSERT_CARD: "Add back-of-note card",
|
||||||
CONVERT_CARD_TO_FILE: "Move back-of-note card to File",
|
CONVERT_CARD_TO_FILE: "Move back-of-note card to File",
|
||||||
@@ -101,6 +104,9 @@ export default {
|
|||||||
FONTS_LOADED: "Excalidraw: CJK Fonts loaded",
|
FONTS_LOADED: "Excalidraw: CJK Fonts loaded",
|
||||||
FONTS_LOAD_ERROR: "Excalidraw: Could not find CJK Fonts in the assets folder\n",
|
FONTS_LOAD_ERROR: "Excalidraw: Could not find CJK Fonts in the assets folder\n",
|
||||||
|
|
||||||
|
//Prompt.ts
|
||||||
|
SELECT_LINK_TO_OPEN: "Select a link to open",
|
||||||
|
|
||||||
//ExcalidrawView.ts
|
//ExcalidrawView.ts
|
||||||
NO_SEARCH_RESULT: "Didn't find a matching element in the drawing",
|
NO_SEARCH_RESULT: "Didn't find a matching element in the drawing",
|
||||||
FORCE_SAVE_ABORTED: "Force Save aborted because saving is in progress",
|
FORCE_SAVE_ABORTED: "Force Save aborted because saving is in progress",
|
||||||
@@ -955,4 +961,7 @@ FILENAME_HEAD: "Filename",
|
|||||||
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_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",
|
IPM_SELECT_PDF: "Please select a PDF file",
|
||||||
|
|
||||||
|
//Utils.ts
|
||||||
|
UPDATE_AVAILABLE: `A newer version of Excalidraw is available in Community Plugins.\n\nYou are using ${PLUGIN_VERSION}.\nThe latest is`,
|
||||||
|
ERROR_PNG_TOO_LARGE: "Error exporting PNG - PNG too large, try a smaller resolution",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import { TAG_AUTOEXPORT, TAG_MDREADINGMODE, TAG_PDFEXPORT } from "src/constants/
|
|||||||
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/ModifierkeyHelper";
|
import { labelALT, labelCTRL, labelMETA, labelSHIFT } from "src/utils/ModifierkeyHelper";
|
||||||
|
|
||||||
const CJK_FONTS = "CJK Fonts";
|
const CJK_FONTS = "CJK Fonts";
|
||||||
|
declare const PLUGIN_VERSION:string;
|
||||||
|
|
||||||
// 简体中文
|
// 简体中文
|
||||||
export default {
|
export default {
|
||||||
// main.ts
|
// main.ts
|
||||||
@@ -76,6 +78,7 @@ export default {
|
|||||||
IMPORT_SVG_CONTEXTMENU: "转换 SVG 到线条 - 有限制",
|
IMPORT_SVG_CONTEXTMENU: "转换 SVG 到线条 - 有限制",
|
||||||
INSERT_MD: "插入 Markdown 文档(以图像形式嵌入)到当前绘图中",
|
INSERT_MD: "插入 Markdown 文档(以图像形式嵌入)到当前绘图中",
|
||||||
INSERT_PDF: "插入 PDF 文档(以图像形式嵌入)到当前绘图中",
|
INSERT_PDF: "插入 PDF 文档(以图像形式嵌入)到当前绘图中",
|
||||||
|
INSERT_LAST_ACTIVE_PDF_PAGE_AS_IMAGE: "将最后激活的 PDF 页面插入为图片",
|
||||||
UNIVERSAL_ADD_FILE: "插入任意文件(以交互形式嵌入,或者以图像形式嵌入)到当前绘图中",
|
UNIVERSAL_ADD_FILE: "插入任意文件(以交互形式嵌入,或者以图像形式嵌入)到当前绘图中",
|
||||||
INSERT_CARD: "插入“背景笔记”卡片",
|
INSERT_CARD: "插入“背景笔记”卡片",
|
||||||
CONVERT_CARD_TO_FILE: "将“背景笔记”卡片保存到文件",
|
CONVERT_CARD_TO_FILE: "将“背景笔记”卡片保存到文件",
|
||||||
@@ -101,6 +104,9 @@ export default {
|
|||||||
FONTS_LOADED : "Excalidraw: CJK 字体已加载" ,
|
FONTS_LOADED : "Excalidraw: CJK 字体已加载" ,
|
||||||
FONTS_LOAD_ERROR : "Excalidraw: 在资源文件夹下找不到 CJK 字体\n" ,
|
FONTS_LOAD_ERROR : "Excalidraw: 在资源文件夹下找不到 CJK 字体\n" ,
|
||||||
|
|
||||||
|
//Prompt.ts
|
||||||
|
SELECT_LINK_TO_OPEN: "选择要打开的链接",
|
||||||
|
|
||||||
//ExcalidrawView.ts
|
//ExcalidrawView.ts
|
||||||
NO_SEARCH_RESULT: "在绘图中未找到匹配的元素",
|
NO_SEARCH_RESULT: "在绘图中未找到匹配的元素",
|
||||||
FORCE_SAVE_ABORTED: "自动保存被中止,因为文件正在保存中",
|
FORCE_SAVE_ABORTED: "自动保存被中止,因为文件正在保存中",
|
||||||
@@ -866,6 +872,7 @@ FILENAME_HEAD: "文件名",
|
|||||||
对此带来的不便,我深表歉意。
|
对此带来的不便,我深表歉意。
|
||||||
</p>
|
</p>
|
||||||
`,
|
`,
|
||||||
|
|
||||||
//ObsidianMenu.tsx
|
//ObsidianMenu.tsx
|
||||||
GOTO_FULLSCREEN: "进入全屏模式",
|
GOTO_FULLSCREEN: "进入全屏模式",
|
||||||
EXIT_FULLSCREEN: "退出全屏模式",
|
EXIT_FULLSCREEN: "退出全屏模式",
|
||||||
@@ -954,4 +961,7 @@ FILENAME_HEAD: "文件名",
|
|||||||
IPM_GROUP_PAGES_DESC: "这将把所有页面建立为一个单独的组。如果您在导入后锁定页面,建议使用此方法,因为这样可以更方便地解锁整个组,而不是逐个解锁。",
|
IPM_GROUP_PAGES_DESC: "这将把所有页面建立为一个单独的组。如果您在导入后锁定页面,建议使用此方法,因为这样可以更方便地解锁整个组,而不是逐个解锁。",
|
||||||
IPM_SELECT_PDF: "请选择一个 PDF 文件",
|
IPM_SELECT_PDF: "请选择一个 PDF 文件",
|
||||||
|
|
||||||
};
|
//Utils.ts
|
||||||
|
UPDATE_AVAILABLE: `Excalidraw 的新版本已在社区插件中可用。\n\n您正在使用 ${PLUGIN_VERSION}。\n最新版本是`,
|
||||||
|
ERROR_PNG_TOO_LARGE: "导出 PNG 时出错 - PNG 文件过大,请尝试较小的分辨率",
|
||||||
|
};
|
||||||
|
|||||||
898
src/main.ts
898
src/main.ts
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,7 @@ export async function insertImageToView(
|
|||||||
file: TFile | string,
|
file: TFile | string,
|
||||||
scale?: boolean,
|
scale?: boolean,
|
||||||
shouldInsertToView: boolean = true,
|
shouldInsertToView: boolean = true,
|
||||||
|
repositionToCursor: boolean = false,
|
||||||
):Promise<string> {
|
):Promise<string> {
|
||||||
if(shouldInsertToView) {ea.clear();}
|
if(shouldInsertToView) {ea.clear();}
|
||||||
ea.style.strokeColor = "transparent";
|
ea.style.strokeColor = "transparent";
|
||||||
@@ -31,7 +32,7 @@ export async function insertImageToView(
|
|||||||
file,
|
file,
|
||||||
scale,
|
scale,
|
||||||
);
|
);
|
||||||
if(shouldInsertToView) {await ea.addElementsToView(false, true, true);}
|
if(shouldInsertToView) {await ea.addElementsToView(repositionToCursor, true, true);}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,37 @@
|
|||||||
//for future use, not used currently
|
//for future use, not used currently
|
||||||
|
|
||||||
import { ImageCrop } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
import { ImageCrop } from "@zsviczian/excalidraw/types/excalidraw/element/types";
|
||||||
import { LinkParts } from "./Utils";
|
|
||||||
|
|
||||||
export function getPDFCropRect (props: {
|
export function getPDFCropRect (props: {
|
||||||
scale: number,
|
scale: number,
|
||||||
linkParts: LinkParts,
|
link: string,
|
||||||
naturalHeight: number,
|
naturalHeight: number,
|
||||||
naturalWidth: number,
|
naturalWidth: number,
|
||||||
}) : ImageCrop | null {
|
}) : ImageCrop | null {
|
||||||
const cropRect = props.linkParts.ref.split("rect=")[1]?.split(",").map(x=>parseInt(x));
|
const rectVal = props.link.match(/&rect=(\d*),(\d*),(\d*),(\d*)/);
|
||||||
const validRect = cropRect && cropRect.length === 4 && cropRect.every(x=>!isNaN(x));
|
if (!rectVal || rectVal.length !== 5) {
|
||||||
|
|
||||||
if(!validRect) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const R0 = parseInt(rectVal[1]);
|
||||||
|
const R1 = parseInt(rectVal[2]);
|
||||||
|
const R2 = parseInt(rectVal[3]);
|
||||||
|
const R3 = parseInt(rectVal[4]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: cropRect[0] * props.scale,
|
x: R0 * props.scale,
|
||||||
y: (props.naturalHeight/props.scale - cropRect[3]) * props.scale,
|
y: (props.naturalHeight/props.scale - R3) * props.scale,
|
||||||
width: (cropRect[2] - cropRect[0]) * props.scale,
|
width: (R2 - R0) * props.scale,
|
||||||
height: (cropRect[3] - cropRect[1]) * props.scale,
|
height: (R3 - R1) * props.scale,
|
||||||
naturalWidth: props.naturalWidth,
|
naturalWidth: props.naturalWidth,
|
||||||
naturalHeight: props.naturalHeight,
|
naturalHeight: props.naturalHeight,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPDFRect(elCrop: ImageCrop, scale: number): string {
|
||||||
|
const R0 = elCrop.x / scale;
|
||||||
|
const R2 = elCrop.width / scale + R0;
|
||||||
|
const R3 = (elCrop.naturalHeight - elCrop.y) / scale;
|
||||||
|
const R1 = R3 - elCrop.height / scale;
|
||||||
|
return `&rect=${Math.round(R0)},${Math.round(R1)},${Math.round(R2)},${Math.round(R3)}`;
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,7 @@ import opentype from 'opentype.js';
|
|||||||
import { runCompressionWorker } from "src/workers/compression-worker";
|
import { runCompressionWorker } from "src/workers/compression-worker";
|
||||||
import Pool from "es6-promise-pool";
|
import Pool from "es6-promise-pool";
|
||||||
import { FileData } from "src/EmbeddedFileLoader";
|
import { FileData } from "src/EmbeddedFileLoader";
|
||||||
|
import { t } from "src/lang/helpers";
|
||||||
|
|
||||||
declare const PLUGIN_VERSION:string;
|
declare const PLUGIN_VERSION:string;
|
||||||
declare var LZString: any;
|
declare var LZString: any;
|
||||||
@@ -77,7 +78,7 @@ export async function checkExcalidrawVersion() {
|
|||||||
|
|
||||||
if (isVersionNewerThanOther(latestVersion,PLUGIN_VERSION)) {
|
if (isVersionNewerThanOther(latestVersion,PLUGIN_VERSION)) {
|
||||||
new Notice(
|
new Notice(
|
||||||
`A newer version of Excalidraw is available in Community Plugins.\n\nYou are using ${PLUGIN_VERSION}.\nThe latest is ${latestVersion}`,
|
t("UPDATE_AVAILABLE") + ` ${latestVersion}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -220,15 +221,6 @@ export async function getFontDataURL (
|
|||||||
const split = dataURL.split(";base64,", 2);
|
const split = dataURL.split(";base64,", 2);
|
||||||
dataURL = `${split[0]};charset=utf-8;base64,${split[1]}`;
|
dataURL = `${split[0]};charset=utf-8;base64,${split[1]}`;
|
||||||
fontDef = ` @font-face {font-family: "${fontName}";src: url("${dataURL}") format("${format}")}`;
|
fontDef = ` @font-face {font-family: "${fontName}";src: url("${dataURL}") format("${format}")}`;
|
||||||
/* const mimeType = f.extension.startsWith("woff")
|
|
||||||
? "application/font-woff"
|
|
||||||
: "font/truetype";
|
|
||||||
fontName = name ?? f.basename;
|
|
||||||
dataURL = await getDataURL(ab, mimeType);
|
|
||||||
fontDef = ` @font-face {font-family: "${fontName}";src: url("${dataURL}")}`;
|
|
||||||
//format("${f.extension === "ttf" ? "truetype" : f.extension}");}`;
|
|
||||||
const split = fontDef.split(";base64,", 2);
|
|
||||||
fontDef = `${split[0]};charset=utf-8;base64,${split[1]}`;*/
|
|
||||||
}
|
}
|
||||||
return { fontDef, fontName, dataURL };
|
return { fontDef, fontName, dataURL };
|
||||||
};
|
};
|
||||||
@@ -375,7 +367,7 @@ export async function getPNG (
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
new Notice("Error exporting PNG - PNG too large, try a smaller resolution");
|
new Notice(t("ERROR_PNG_TOO_LARGE"));
|
||||||
errorlog({ where: "Utils.getPNG", error });
|
errorlog({ where: "Utils.getPNG", error });
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -477,8 +469,6 @@ export function scaleLoadedImage (
|
|||||||
const ratioX = elCrop.x / (elCrop.naturalWidth - elCrop.x - elCrop.width);
|
const ratioX = elCrop.x / (elCrop.naturalWidth - elCrop.x - elCrop.width);
|
||||||
const gapX = imgWidth - elCrop.width;
|
const gapX = imgWidth - elCrop.width;
|
||||||
el.crop.x = ratioX * gapX / (1 + ratioX);
|
el.crop.x = ratioX * gapX / (1 + ratioX);
|
||||||
// const ratioA = elCrop.x / (elCrop.naturalWidth - elCrop.x);
|
|
||||||
// el.crop.x = ratioA * imgWidth / (1 + ratioA);
|
|
||||||
if(el.crop.x + elCrop.width > imgWidth) {
|
if(el.crop.x + elCrop.width > imgWidth) {
|
||||||
el.crop.x = (imgWidth - elCrop.width) / 2;
|
el.crop.x = (imgWidth - elCrop.width) / 2;
|
||||||
}
|
}
|
||||||
@@ -492,8 +482,6 @@ export function scaleLoadedImage (
|
|||||||
const ratioY = elCrop.y / (elCrop.naturalHeight - elCrop.y - elCrop.height);
|
const ratioY = elCrop.y / (elCrop.naturalHeight - elCrop.y - elCrop.height);
|
||||||
const gapY = imgHeight - elCrop.height;
|
const gapY = imgHeight - elCrop.height;
|
||||||
el.crop.y = ratioY * gapY / (1 + ratioY);
|
el.crop.y = ratioY * gapY / (1 + ratioY);
|
||||||
// const ratioB = elCrop.y / (elCrop.naturalHeight - elCrop.y);
|
|
||||||
// el.crop.y = ratioB * imgHeight / (1 + ratioB);
|
|
||||||
if(el.crop.y + elCrop.height > imgHeight) {
|
if(el.crop.y + elCrop.height > imgHeight) {
|
||||||
el.crop.y = (imgHeight - elCrop.height)/2;
|
el.crop.y = (imgHeight - elCrop.height)/2;
|
||||||
}
|
}
|
||||||
@@ -777,6 +765,8 @@ export function getPNGScale (plugin: ExcalidrawPlugin, file: TFile): number {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function isVersionNewerThanOther (version: string, otherVersion: string): boolean {
|
export function isVersionNewerThanOther (version: string, otherVersion: string): boolean {
|
||||||
|
if(!version || !otherVersion) return true;
|
||||||
|
|
||||||
const v = version.match(/(\d*)\.(\d*)\.(\d*)/);
|
const v = version.match(/(\d*)\.(\d*)\.(\d*)/);
|
||||||
const o = otherVersion.match(/(\d*)\.(\d*)\.(\d*)/);
|
const o = otherVersion.match(/(\d*)\.(\d*)\.(\d*)/);
|
||||||
|
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ label.color-input-container > input {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.excalidraw-settings input:not([type="color"]) {
|
.excalidraw-settings input[type="text"] {
|
||||||
min-width: 10em;
|
min-width: 10em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user