diff --git a/README.md b/README.md index f5b08a9..831b0d1 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,8 @@ Please upgrade to Obsidian v0.12.19 or higher to get the latest release. - Insert LaTeX formulas using the Command Palette action "Insert LaTeX formula". You can edit formulas either in Markdown view, or by CTRL/CMD + Click on the formula. - Drag & Drop support - You can drag files from the Obsidian file explorer and they will become links to those files in Excalidraw. - - Dragging image files (PNG, SVG, JPG, Excalidraw) from Obsidian's file explorer while pressing the CTRL/CMD button will embed the image into your drawing. - - You can drag and drop images from outside Obsidian onto Excalidraw. These images will be embedded into your drawing and saved to Obsidian. + - Dragging image files (PNG, SVG, JPG, ICO, GIF, WEBP, Excalidraw) from Obsidian's file explorer while pressing the CTRL (SHIFT on Mac) button will embed the image into your drawing. + - If in addition to CTRL or SHIFT you also hold down ALT, the image will be inserted at 100% of its size. ⚠ Note: this is a very niche feature with a very particular behavior that I built primarily for myself (even more so than other features in Excalidraw Obsidian - also built primarily for myself 😉)... This will reset your embedded image to 100% size every time you open the Excalidraw drawing, or in case you have embedded an Excalidraw drawing on your canvas inserted using this function, every time you update the embedded drawing, it will be scaled back to 100% size. This means that even if you resize the image on the drawing, it will reset to 100% the next time you open the file or you modify the original embedded object. This feature is useful when you decompose a drawing into separate Excalidraw files, but when combined onto a single canvas you want the individual pieces to maintain their actual sizes. I use this feature to construct Book-on-a-Page summaries from atomic drawings. - You can drag and drop text from Markdown views onto Excalidraw. - You can drag and drop web addresses from your browser and they will become links. - Image support diff --git a/src/EmbeddedFileLoader.ts b/src/EmbeddedFileLoader.ts index 6778f10..df9e424 100644 --- a/src/EmbeddedFileLoader.ts +++ b/src/EmbeddedFileLoader.ts @@ -48,6 +48,7 @@ export declare type MimeType = export type FileData = BinaryFileData & { size: Size; hasSVGwithBitmap: boolean; + shouldScale: boolean; //true if image should maintain its area, false if image should display at 100% its size }; export type Size = { @@ -182,6 +183,14 @@ export class EmbeddedFile { } return this.img; //images that are not SVGwithBitmap, only the light string is stored, since inverted and non-inverted are === } + + /** + * + * @returns true if image should scale such as the updated images has the same area as the previous images, false if the image should be displayed at 100% + */ + public shouldScale() { + return !Boolean(this.linkParts && this.linkParts.original && this.linkParts.original.endsWith("|100%")); + } } export class EmbeddedFilesLoader { @@ -367,6 +376,7 @@ export class EmbeddedFilesLoader { created: data.created, size: data.size, hasSVGwithBitmap: data.hasSVGwithBitmap, + shouldScale: embeddedFile.shouldScale() }); } } else if (embeddedFile.isSVGwithBitmap) { @@ -377,6 +387,7 @@ export class EmbeddedFilesLoader { created: embeddedFile.mtime, size: embeddedFile.size, hasSVGwithBitmap: embeddedFile.isSVGwithBitmap, + shouldScale: embeddedFile.shouldScale() }); } } @@ -395,6 +406,7 @@ export class EmbeddedFilesLoader { created: data.created, size: data.size, hasSVGwithBitmap: false, + shouldScale: true }); } } diff --git a/src/ExcalidrawAutomate.ts b/src/ExcalidrawAutomate.ts index 05fd7ff..c411cbb 100644 --- a/src/ExcalidrawAutomate.ts +++ b/src/ExcalidrawAutomate.ts @@ -894,6 +894,7 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { topX: number, topY: number, imageFile: TFile, + scale: boolean = true, //true will scale the image to MAX_IMAGE_SIZE, false will insert image at 100% of its size ): Promise { const id = nanoid(); const loader = new EmbeddedFilesLoader( @@ -910,11 +911,11 @@ export class ExcalidrawAutomate implements ExcalidrawAutomateInterface { id: fileId, dataURL: image.dataURL, created: image.created, - file: imageFile.path, + file: imageFile.path + (scale ? "":"|100%"), hasSVGwithBitmap: image.hasSVGwithBitmap, latex: null, }; - if (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 = MAX_IMAGE_SIZE / Math.max(image.size.width, image.size.height); image.size.width = scale * image.size.width; diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index af2a0a3..44bc16e 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -2926,6 +2926,7 @@ export default class ExcalidrawView extends TextFileView { currentPosition.x, currentPosition.y, draggable.file, + !event.altKey, ); ea.addElementsToView(false, false, true); })(); @@ -2955,6 +2956,7 @@ export default class ExcalidrawView extends TextFileView { currentPosition.x + counter*50, currentPosition.y + counter*50, f, + !event.altKey, ); counter++; await ea.addElementsToView(false, false, true); diff --git a/src/LaTeX.ts b/src/LaTeX.ts index e4f9e68..9eccbe4 100644 --- a/src/LaTeX.ts +++ b/src/LaTeX.ts @@ -27,6 +27,7 @@ export const updateEquation = async ( created: data.created, size: data.size, hasSVGwithBitmap: false, + shouldScale: true, }); addFiles(files, view); } diff --git a/src/dialogs/InsertImageDialog.ts b/src/dialogs/InsertImageDialog.ts index b6a7390..c4200d2 100644 --- a/src/dialogs/InsertImageDialog.ts +++ b/src/dialogs/InsertImageDialog.ts @@ -17,12 +17,23 @@ export class InsertImageDialog extends FuzzySuggestModal { this.limit = 20; this.setInstructions([ { - command: t("SELECT_FILE"), + command: t("SELECT_FILE_WITH_OPTION_TO_SCALE"), purpose: "", }, ]); this.setPlaceholder(t("SELECT_DRAWING")); this.emptyStateText = t("NO_MATCH"); + this.inputEl.onkeyup = (e) => { + //@ts-ignore + if (e.key === "Enter" && e.altKey && this.chooser.values) { + this.onChooseItem( + //@ts-ignore + this.chooser.values[this.chooser.selectedItem].item, + new KeyboardEvent("keypress",{altKey: true}) + ); + this.close(); + } + } } getItems(): TFile[] { @@ -39,13 +50,13 @@ export class InsertImageDialog extends FuzzySuggestModal { return item.path; } - onChooseItem(item: TFile): void { + onChooseItem(item: TFile, event: KeyboardEvent): void { const ea = this.plugin.ea; ea.reset(); ea.setView(this.view); ea.canvas.theme = this.view.excalidrawAPI.getAppState().theme; (async () => { - await ea.addImage(0, 0, item); + await ea.addImage(0, 0, item, !event.altKey); ea.addElementsToView(true, false, true); })(); } diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index a2e0ebd..e1f31a2 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -52,7 +52,7 @@ export default { INSERT_LINK_TO_ELEMENT_ERROR: "Select a single element in the scene", INSERT_LINK_TO_ELEMENT_READY: "Link is READY and available on the clipboard", INSERT_LINK: "Insert link to file", - INSERT_IMAGE: "Insert image from vault", + INSERT_IMAGE: "Insert image or Excalidraw drawing from your vault", INSERT_MD: "Insert markdown file from vault", INSERT_LATEX: "Insert LaTeX formula (e.g. \\binom{n}{k} = \\frac{n!}{k!(n-k)!})", @@ -418,9 +418,10 @@ export default { //openDrawings.ts SELECT_FILE: "Select a file then press enter.", + SELECT_FILE_WITH_OPTION_TO_SCALE: "Select a file then press ENTER, or ALT+ENTER to insert at 100% scale.", NO_MATCH: "No file matches your query.", SELECT_FILE_TO_LINK: "Select the file you want to insert the link for.", - SELECT_DRAWING: "Select the drawing you want to insert", + SELECT_DRAWING: "Select the image or drawing you want to insert", TYPE_FILENAME: "Type name of drawing to select.", SELECT_FILE_OR_TYPE_NEW: "Select existing drawing or type name of a new drawing then press Enter.", diff --git a/src/settings.ts b/src/settings.ts index 2f54482..1d6f101 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -16,6 +16,7 @@ import { getEmbedFilename, } from "./utils/FileUtils"; import { + fragWithHTML, setLeftHandedMode, } from "./utils/Utils"; @@ -198,9 +199,6 @@ export const DEFAULT_SETTINGS: ExcalidrawSettings = { mathjaxSourceURL: "https://cdn.jsdelivr.net/npm/mathjax@3.2.1/es5/tex-svg.js" }; -const fragWithHTML = (html: string) => - createFragment((frag) => (frag.createDiv().innerHTML = html)); - export class ExcalidrawSettingTab extends PluginSettingTab { plugin: ExcalidrawPlugin; private requestEmbedUpdate: boolean = false; diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 92db7e5..1af8e03 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -383,19 +383,29 @@ export const scaleLoadedImage = ( .filter((e: any) => e.type === "image" && e.fileId === f.id) .forEach((el: any) => { const [w_old, h_old] = [el.width, el.height]; - const elementAspectRatio = w_old / h_old; - if (imageAspectRatio != elementAspectRatio) { - dirty = true; - const h_new = Math.sqrt((w_old * h_old * h_image) / w_image); - const w_new = Math.sqrt((w_old * h_old * w_image) / h_image); - el.height = h_new; - el.width = w_new; - el.y += (h_old - h_new) / 2; - el.x += (w_old - w_new) / 2; + if(f.shouldScale) { + const elementAspectRatio = w_old / h_old; + if (imageAspectRatio != elementAspectRatio) { + dirty = true; + const h_new = Math.sqrt((w_old * h_old * h_image) / w_image); + const w_new = Math.sqrt((w_old * h_old * w_image) / h_image); + el.height = h_new; + el.width = w_new; + el.y += (h_old - h_new) / 2; + el.x += (w_old - w_new) / 2; + } + } else { + if(w_old !== w_image || h_old !== h_image) { + dirty = true; + el.height = h_image; + el.width = w_image; + el.y += (h_old - h_image) / 2; + el.x += (w_old - w_image) / 2; + } } }); - return { dirty, scene }; } + return { dirty, scene }; }; export const setDocLeftHandedMode = (isLeftHanded: boolean, ownerDocument:Document) => { @@ -622,6 +632,9 @@ export const getEmbeddedFilenameParts = (fname:string):{ } } +export const fragWithHTML = (html: string) => + createFragment((frag) => (frag.createDiv().innerHTML = html)); + export const errorlog = (data: {}) => { console.error({ plugin: "Excalidraw", ...data }); };