mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
2.0.17
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-excalidraw-plugin",
|
||||
"name": "Excalidraw",
|
||||
"version": "2.0.16",
|
||||
"version": "2.0.17",
|
||||
"minAppVersion": "1.1.6",
|
||||
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
|
||||
"author": "Zsolt Viczian",
|
||||
|
||||
@@ -350,6 +350,7 @@ export class EmbeddedFilesLoader {
|
||||
elements?: ExcalidrawElement[];
|
||||
}) : Promise<{dataURL: DataURL, hasSVGwithBitmap:boolean}> {
|
||||
//debug({where:"EmbeddedFileLoader.getExcalidrawSVG",uid:this.uid,file:file.name});
|
||||
const isMask = isMaskFile(this.plugin, file);
|
||||
const forceTheme = hasExportTheme(this.plugin, file)
|
||||
? getExportTheme(this.plugin, file, "light")
|
||||
: undefined;
|
||||
@@ -358,7 +359,7 @@ export class EmbeddedFilesLoader {
|
||||
? getWithBackground(this.plugin, file)
|
||||
: false,
|
||||
withTheme: !!forceTheme,
|
||||
isMask: isMaskFile(this.plugin,file),
|
||||
isMask,
|
||||
};
|
||||
const svg = replaceSVGColors(
|
||||
await createSVG(
|
||||
|
||||
@@ -682,7 +682,11 @@ export class ExcalidrawAutomate {
|
||||
outString += `${key}: $$${item.latex}$$\n`;
|
||||
} else {
|
||||
if(item.file) {
|
||||
outString += `${key}: [[${item.file}]]\n`;
|
||||
if(item.file instanceof TFile) {
|
||||
outString += `${key}: [[${item.file.path}]]\n`;
|
||||
} else {
|
||||
outString += `${key}: [[${item.file}]]\n`;
|
||||
}
|
||||
} else {
|
||||
outString += `${key}: ${item.hyperlink}\n`;
|
||||
}
|
||||
|
||||
@@ -4014,9 +4014,9 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getSingleSelectedImageWithURL(): {imageEl: ExcalidrawImageElement, embeddedFile: EmbeddedFile} {
|
||||
public getSingleSelectedImage(): {imageEl: ExcalidrawImageElement, embeddedFile: EmbeddedFile} {
|
||||
if(!this.excalidrawAPI) return null;
|
||||
const els = this.getViewSelectedElements().filter(el=>el.type==="image");
|
||||
if(els.length !== 1) {
|
||||
@@ -4024,7 +4024,6 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
const el = els[0] as ExcalidrawImageElement;
|
||||
const imageFile = this.excalidrawData.getFile(el.fileId);
|
||||
if(!imageFile?.isHyperLink) return null;
|
||||
return {imageEl: el, embeddedFile: imageFile};
|
||||
}
|
||||
|
||||
@@ -4163,19 +4162,38 @@ export default class ExcalidrawView extends TextFileView {
|
||||
}
|
||||
}
|
||||
|
||||
const img = this.getSingleSelectedImageWithURL();
|
||||
if(img) {
|
||||
const img = this.getSingleSelectedImage();
|
||||
if(img && img.embeddedFile?.isHyperLink) {
|
||||
contextMenuActions.push([
|
||||
renderContextMenuAction(
|
||||
t("CONVERT_URL_TO_FILE"),
|
||||
() => {
|
||||
this.convertImageElWithURLToLocalFile(img);
|
||||
setTimeout(()=>this.convertImageElWithURLToLocalFile(img));
|
||||
},
|
||||
onClose
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
if(img && img.embeddedFile && img.embeddedFile.mimeType === "image/svg+xml") {
|
||||
contextMenuActions.push([
|
||||
renderContextMenuAction(
|
||||
t("IMPORT_SVG_CONTEXTMENU"),
|
||||
() => {
|
||||
const base64Content = img.embeddedFile.getImage(false).split(',')[1];
|
||||
// Decoding the base64 content
|
||||
const svg = atob(base64Content);
|
||||
if(!svg || svg === "") return;
|
||||
const ea = getEA(this) as ExcalidrawAutomate;
|
||||
ea.importSVG(svg);
|
||||
ea.addToGroup(ea.getElements().map(el=>el.id));
|
||||
ea.addElementsToView(true, true, true,true);
|
||||
},
|
||||
onClose
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
contextMenuActions.push([
|
||||
renderContextMenuAction(
|
||||
t("UNIVERSAL_ADD_FILE"),
|
||||
|
||||
@@ -3,6 +3,8 @@ import { REG_LINKINDEX_INVALIDCHARS } from "../constants/constants";
|
||||
import ExcalidrawView from "../ExcalidrawView";
|
||||
import { t } from "../lang/helpers";
|
||||
import ExcalidrawPlugin from "../main";
|
||||
import { getEA } from "src";
|
||||
import { ExcalidrawAutomate } from "src/ExcalidrawAutomate";
|
||||
|
||||
export class ImportSVGDialog extends FuzzySuggestModal<TFile> {
|
||||
public app: App;
|
||||
@@ -38,12 +40,11 @@ export class ImportSVGDialog extends FuzzySuggestModal<TFile> {
|
||||
|
||||
async onChooseItem(item: TFile, event: KeyboardEvent): Promise<void> {
|
||||
if(!item) return;
|
||||
const ea = this.plugin.ea;
|
||||
ea.reset();
|
||||
ea.setView(this.view);
|
||||
const ea = getEA(this.view) as ExcalidrawAutomate;
|
||||
const svg = await app.vault.read(item);
|
||||
if(!svg || svg === "") return;
|
||||
ea.importSVG(svg);
|
||||
ea.addToGroup(ea.getElements().map(el=>el.id));
|
||||
ea.addElementsToView(true, true, true,true);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,16 @@ I develop this plugin as a hobby, spending my free time doing this. If you find
|
||||
|
||||
<div class="ex-coffee-div"><a href="https://ko-fi.com/zsolt"><img src="https://cdn.ko-fi.com/cdn/kofi3.png?v=3" height=45></a></div>
|
||||
`,
|
||||
"2.0.17":`
|
||||
## Fixed
|
||||
- Image cropping now supports dark mode
|
||||
- Image cropping/carve out was not working reliably in some cases [#1546](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1546)
|
||||
- Masking a mirrored image resulted in an off-positioned mask
|
||||
|
||||
## New
|
||||
- Context menu action to convert SVG to Excalidraw strokes
|
||||
- Updated Chinese translation (Thank you @tswwe)
|
||||
`,
|
||||
"2.0.16":`
|
||||
## Fixed
|
||||
- Image cropping did not work consistently with large image files on lower-powered devices [#1538](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues/1538).
|
||||
|
||||
@@ -66,6 +66,7 @@ export default {
|
||||
INSERT_COMMAND: "Insert Obsidian Command as a link",
|
||||
INSERT_IMAGE: "Insert image or Excalidraw drawing from your vault",
|
||||
IMPORT_SVG: "Import an SVG file as Excalidraw strokes (limited SVG support, TEXT currently not supported)",
|
||||
IMPORT_SVG_CONTEXTMENU: "Convert SVG to strokes - with limitations",
|
||||
INSERT_MD: "Insert markdown file from vault",
|
||||
INSERT_PDF: "Insert PDF file from vault",
|
||||
UNIVERSAL_ADD_FILE: "Insert ANY file",
|
||||
|
||||
@@ -808,8 +808,8 @@ export default class ExcalidrawPlugin extends Plugin {
|
||||
checkCallback: (checking: boolean) => {
|
||||
const view = this.app.workspace.getActiveViewOfType(ExcalidrawView);
|
||||
if(!view) return false;
|
||||
const img = view.getSingleSelectedImageWithURL();
|
||||
if(!img) return false;
|
||||
const img = view.getSingleSelectedImage();
|
||||
if(!img || !img.embeddedFile?.isHyperLink) return false;
|
||||
if(checking) return true;
|
||||
view.convertImageElWithURLToLocalFile(img);
|
||||
},
|
||||
|
||||
@@ -25,6 +25,8 @@ export const carveOutImage = async (sourceEA: ExcalidrawAutomate, viewImageEl: E
|
||||
newImage.y = 0;
|
||||
newImage.width = width;
|
||||
newImage.height = height;
|
||||
const scale = newImage.scale;
|
||||
newImage.scale = [1,1];
|
||||
|
||||
const ef = sourceEA.targetView.excalidrawData.getFile(viewImageEl.fileId);
|
||||
let imageLink = "";
|
||||
@@ -46,6 +48,7 @@ export const carveOutImage = async (sourceEA: ExcalidrawAutomate, viewImageEl: E
|
||||
const file = await createImageCropperFile(targetEA, newImage.id, imageLink, foldername, filename);
|
||||
if(!file) return;
|
||||
|
||||
//console.log(await app.vault.read(file));
|
||||
sourceEA.clear();
|
||||
sourceEA.copyViewElementsToEAforEditing([viewImageEl]);
|
||||
const sourceImageEl = sourceEA.getElement(viewImageEl.id) as Mutable<ExcalidrawImageElement>;
|
||||
@@ -55,6 +58,7 @@ export const carveOutImage = async (sourceEA: ExcalidrawAutomate, viewImageEl: E
|
||||
const replacingImage = sourceEA.getElement(replacingImageID) as Mutable<ExcalidrawImageElement>;
|
||||
replacingImage.width = sourceImageEl.width;
|
||||
replacingImage.height = sourceImageEl.height;
|
||||
replacingImage.scale = scale;
|
||||
sourceEA.addElementsToView(false, true, true);
|
||||
}
|
||||
|
||||
@@ -108,7 +112,7 @@ export const createImageCropperFile = async (targetEA: ExcalidrawAutomate, image
|
||||
//wait for file to be created/indexed by Obsidian
|
||||
let file = vault.getAbstractFileByPath(newPath);
|
||||
let counter = 0;
|
||||
while(!file && counter < 50) {
|
||||
while((!file || !targetEA.isExcalidrawFile(file as TFile)) && counter < 50) {
|
||||
await sleep(100);
|
||||
file = vault.getAbstractFileByPath(newPath);
|
||||
counter++;
|
||||
@@ -119,6 +123,7 @@ export const createImageCropperFile = async (targetEA: ExcalidrawAutomate, image
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
//wait for the new ExcalidrawView to open and initialize
|
||||
counter = 0;
|
||||
let newView = workspace.getActiveViewOfType(ExcalidrawView) as ExcalidrawView;
|
||||
@@ -151,7 +156,7 @@ export const createImageCropperFile = async (targetEA: ExcalidrawAutomate, image
|
||||
new Notice("Image did not load to the view. NewExcalidraw Drawing is taking too long to load. Please try again.");
|
||||
return;
|
||||
}
|
||||
|
||||
*/
|
||||
//console.log({counter, path: workspace.getActiveFile()?.path, newView, files: api.getFiles()});
|
||||
|
||||
return file;
|
||||
|
||||
@@ -91,13 +91,17 @@ export class CropImage {
|
||||
return {style, mask:maskSVG.innerHTML};
|
||||
}
|
||||
|
||||
private async getImageSVG() {
|
||||
private async getImage() {
|
||||
const exportSettings:ExportSettings = {
|
||||
withBackground: false,
|
||||
withTheme: false,
|
||||
isMask: false,
|
||||
}
|
||||
|
||||
const images = Object.values(this.imageEA.imagesDict);
|
||||
if(images.length === 1) {
|
||||
return images[0].dataURL;
|
||||
}
|
||||
return await this.imageEA.createPNGBase64(null,1,exportSettings,null,null,0);
|
||||
const imageSVG = await this.imageEA.createSVG(null,false,exportSettings,null,null,0);
|
||||
const svgData = new XMLSerializer().serializeToString(imageSVG);
|
||||
return `data:image/svg+xml;base64,${btoa(unescape(encodeURIComponent(svgData)))}`;
|
||||
@@ -111,12 +115,14 @@ export class CropImage {
|
||||
return;
|
||||
}
|
||||
const maskID = nanoid();
|
||||
const imageID = nanoid();
|
||||
const {viewBox, vbWidth, vbHeight, width, height} = this.getViewBoxAndSize();
|
||||
const parser = new DOMParser();
|
||||
const {style, mask} = await this.getMaskSVG();
|
||||
const svgString = `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="${viewBox}" width="${vbWidth}" height="${vbHeight}">\n` +
|
||||
`<symbol id="${imageID}"><image width="100%" height="100%" href="${await this.getImage()}"/></symbol>\n` +
|
||||
`<defs>${style}\n<mask id="${maskID}" x="0" y="0" width="${width}" height="${height}" maskUnits="userSpaceOnUse">\n${mask}\n</mask>\n</defs>\n` +
|
||||
`<image x="0" y="0" width="${width}" height="${height}" mask="url(#${maskID})" mask-type="alpha" href="${await this.getImageSVG()}"/>\n</svg>`;
|
||||
`<use x="0" y="0" width="${width}" height="${height}" mask="url(#${maskID})" mask-type="alpha" href="#${imageID}"/>\n</svg>`;
|
||||
return parser.parseFromString(
|
||||
svgString,
|
||||
"image/svg+xml",
|
||||
|
||||
Reference in New Issue
Block a user