From d9306922c3f32e6be3a92ef09eee945a029b3801 Mon Sep 17 00:00:00 2001 From: Zsolt Viczian Date: Tue, 19 Oct 2021 22:59:31 +0200 Subject: [PATCH] save image to vault --- package.json | 80 +++++++++++++++++++++---------------------- src/ExcalidrawData.ts | 33 +++++++++++++++--- src/Utils.ts | 58 +++++++++++++++++++++++-------- src/main.ts | 21 +++--------- 4 files changed, 116 insertions(+), 76 deletions(-) diff --git a/package.json b/package.json index 847065a..9475ca1 100644 --- a/package.json +++ b/package.json @@ -1,40 +1,40 @@ -{ - "name": "obsidian-excalidraw-plugin", - "version": "1.3.21", - "description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings", - "main": "main.js", - "scripts": { - "dev": "cross-env NODE_ENV=development rollup --config rollup.config.js -w", - "build": "cross-env NODE_ENV=production rollup --config rollup.config.js" - }, - "keywords": [], - "author": "", - "license": "MIT", - "dependencies": { - "@zsviczian/excalidraw": "0.9.0-obsidian-image-support-11", - "monkey-around": "^2.2.0", - "react": "^17.0.2", - "react-dom": "^17.0.2", - "react-scripts": "^1.1.5", - "roughjs": "4.4.1" - }, - "devDependencies": { - "@babel/core": "^7.15.5", - "@babel/preset-env": "^7.15.6", - "@babel/preset-react": "^7.14.5", - "@rollup/plugin-babel": "^5.3.0", - "@rollup/plugin-commonjs": "^21.0.0", - "@rollup/plugin-node-resolve": "^13.0.5", - "@rollup/plugin-replace": "^2.4.2", - "@rollup/plugin-typescript": "^8.2.5", - "@types/node": "^15.12.4", - "@types/react-dom": "^17.0.9", - "cross-env": "^7.0.3", - "nanoid": "^3.1.23", - "obsidian": "^0.12.16", - "rollup": "^2.52.3", - "rollup-plugin-visualizer": "^5.5.2", - "tslib": "^2.3.1", - "typescript": "^4.4.3" - } -} +{ + "name": "obsidian-excalidraw-plugin", + "version": "1.3.21", + "description": "This is an Obsidian.md plugin that lets you view and edit Excalidraw drawings", + "main": "main.js", + "scripts": { + "dev": "cross-env NODE_ENV=development rollup --config rollup.config.js -w", + "build": "cross-env NODE_ENV=production rollup --config rollup.config.js" + }, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "@zsviczian/excalidraw": "0.9.0-obsidian-image-support-11", + "monkey-around": "^2.2.0", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "react-scripts": "^1.1.5", + "roughjs": "4.4.1" + }, + "devDependencies": { + "@babel/core": "^7.15.5", + "@babel/preset-env": "^7.15.6", + "@babel/preset-react": "^7.14.5", + "@rollup/plugin-babel": "^5.3.0", + "@rollup/plugin-commonjs": "^21.0.0", + "@rollup/plugin-node-resolve": "^13.0.5", + "@rollup/plugin-replace": "^2.4.2", + "@rollup/plugin-typescript": "^8.2.5", + "@types/node": "^15.12.4", + "@types/react-dom": "^17.0.9", + "cross-env": "^7.0.3", + "nanoid": "^3.1.23", + "obsidian": "^0.12.16", + "rollup": "^2.52.3", + "rollup-plugin-visualizer": "^5.5.2", + "tslib": "^2.3.1", + "typescript": "^4.4.3" + } +} diff --git a/src/ExcalidrawData.ts b/src/ExcalidrawData.ts index 36db29c..4351d3b 100644 --- a/src/ExcalidrawData.ts +++ b/src/ExcalidrawData.ts @@ -11,7 +11,7 @@ import { JSON_parse } from "./constants"; import { TextMode } from "./ExcalidrawView"; -import { wrapText } from "./Utils"; +import { getAttachmentsFolderAndFilePath, getBinaryFileFromDataURL, wrapText } from "./Utils"; import { FileId } from "@zsviczian/excalidraw/types/element/types"; @@ -82,7 +82,7 @@ export class ExcalidrawData { private textMode: TextMode = TextMode.raw; private plugin: ExcalidrawPlugin; public loaded: boolean = false; - public files:Map = null; //fileId, path + public files:Map = null; //fileId, path constructor(plugin: ExcalidrawPlugin) { this.plugin = plugin; @@ -98,7 +98,7 @@ export class ExcalidrawData { this.loaded = false; this.file = file; this.textElements = new Map(); - this.files = new Map(); + this.files = new Map(); //I am storing these because if the settings change while a drawing is open parsing will run into errors during save //The drawing will use these values until next drawing is loaded or this drawing is re-loaded @@ -451,10 +451,35 @@ export class ExcalidrawData { return outString + this.plugin.getMarkdownDrawingSection(JSON.stringify(this.scene,null,"\t")); } + private async syncFiles(scene:any):Promise { + //no images to process + if(!scene.appState.files || scene.appState.files == {}) return false; + let dirty = false; + for(const key of Object.keys(scene.appState.files)) { + if(!this.files.has(key)) { + dirty = true; + let fname = "Pasted Image "+window.moment().format("YYYYMMDDHHmmss_SSS"); + switch(scene.appState.files[key].mimeType) { + case "image/png": fname += ".png"; break; + case "image/jpeg": fname += ".jpg"; break; + case "image/svg": fname += ".svg"; break; + case "image/gif": fname += ".gif"; break; + default: fname += ".png"; + } + const [folder,filepath] = await getAttachmentsFolderAndFilePath(this.app,this.file.path,fname); + await this.app.vault.createBinary(filepath,getBinaryFileFromDataURL(scene.appState.files[key].dataURL)); + this.files.set(key,filepath); + } + } + return dirty; + } + public async syncElements(newScene:any):Promise { //console.log("Excalidraw.Data.syncElements()"); + let result = await this.syncFiles(newScene); this.scene = newScene;//JSON_parse(newScene); - const result = this.setLinkPrefix() || this.setUrlPrefix() || this.setShowLinkBrackets(); + this.scene.appState.files = {}; + result = result || this.setLinkPrefix() || this.setUrlPrefix() || this.setShowLinkBrackets(); await this.updateTextElementsFromScene(); return result || this.findNewTextElementsInScene(); } diff --git a/src/Utils.ts b/src/Utils.ts index c0a21c0..3339eac 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -11,7 +11,11 @@ declare module "obsidian" { interface Workspace { getAdjacentLeafInDirection(leaf: WorkspaceLeaf, direction: string): WorkspaceLeaf; } + interface Vault { + getConfig(option:"attachmentFolderPath"): string; + } } + declare let window: ExcalidrawAutomate; /** @@ -182,7 +186,7 @@ export const getNewOrAdjacentLeaf = (plugin: ExcalidrawPlugin, leaf: WorkspaceLe export const getObsidianImage = async (app: App, file: TFile) :Promise<{ - mimeType: "image/svg+xml" | "image/png" | "image/jpeg" | "image/gif" | "application/octet-stream", + mimeType: string; //"image/svg+xml" | "image/png" | "image/jpeg" | "image/gif" | "application/octet-stream", fileId: string, dataURL: string, created: number, @@ -197,20 +201,17 @@ export const getObsidianImage = async (app: App, file: TFile) const excalidrawSVG = isExcalidrawFile ? svgToBase64((await window.ExcalidrawAutomate.createSVG(file.path,true)).outerHTML) : null; - const mimeType = isExcalidrawFile ? - "image/svg+xml" : ( - file.extension === "png" ? - "image/png" : ( - file.extension === "jpg" || file.extension === "jpeg" ? - "image/jpeg" : ( - file.extension === "svg" ? - "image/svg+xml" : ( - file.extension === "gif" ? - "image/gif" : "application/octet-stream" - ) - ) - ) - ); + let mimeType = "image/svg+xml"; + if (!isExcalidrawFile) { + switch (file.extension) { + case "png": mimeType = "image/png";break; + case "jpeg":mimeType = "image/jpeg";break; + case "jpg": mimeType = "image/jpeg";break; + case "gif": mimeType = "image/gif";break; + case "svg": mimeType = "image/svg+xml";break; + default: mimeType = "application/octet-stream"; + } + } return { mimeType: mimeType, fileId: await generateIdFromFile(ab), @@ -268,4 +269,31 @@ const getImageSize = async (app: App, src:string):Promise<{height:number, width: img.onerror = reject; img.src = src; }) +} + +export const getBinaryFileFromDataURL = (dataURL:string):ArrayBuffer => { + if(!dataURL) return null; + const parts = dataURL.matchAll(/base64,(.*)/g).next(); + const binary_string = window.atob(parts.value[1]); + const len = binary_string.length; + const bytes = new Uint8Array(len); + for (var i = 0; i < len; i++) { + bytes[i] = binary_string.charCodeAt(i); + } + return bytes.buffer; +} + +export const getAttachmentsFolderAndFilePath = async (app:App, activeViewFilePath:string, newFileName:string):Promise<[string,string]> => { + let folder = app.vault.getConfig("attachmentFolderPath"); + // folder == null: save to vault root + // folder == "./" save to same folder as current file + // folder == "folder" save to specific folder in vault + // folder == "./folder" save to specific subfolder of current active folder + if(folder && folder.startsWith("./")) { // folder relative to current file + const activeFileFolder = splitFolderAndFilename(activeViewFilePath).folderpath + "/"; + folder = normalizePath(activeFileFolder + folder.substring(2)); + } + if(!folder) folder = ""; + await checkAndCreateFolder(app.vault,folder); + return [folder,normalizePath(folder + "/" + newFileName)]; } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index c01ecaf..9f008c4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -56,15 +56,12 @@ import { Prompt } from "./Prompt"; import { around } from "monkey-around"; import { t } from "./lang/helpers"; import { MigrationPrompt } from "./MigrationPrompt"; -import { checkAndCreateFolder, download, getIMGPathFromExcalidrawFile, getNewUniqueFilepath, splitFolderAndFilename, svgToBase64 } from "./Utils"; +import { checkAndCreateFolder, download, getAttachmentsFolderAndFilePath, getIMGPathFromExcalidrawFile, getNewUniqueFilepath, splitFolderAndFilename, svgToBase64 } from "./Utils"; declare module "obsidian" { interface App { isMobile():boolean; } - interface Vault { - getConfig(option:"attachmentFolderPath"): string; - } interface Workspace { on(name: 'hover-link', callback: (e:MouseEvent) => any, ctx?: any): EventRef; } @@ -577,21 +574,11 @@ export default class ExcalidrawPlugin extends Plugin { const insertDrawingToDoc = async (inNewPane:boolean) => { const activeView = this.app.workspace.getActiveViewOfType(MarkdownView); if(!activeView) return; - let folder = this.app.vault.getConfig("attachmentFolderPath"); - // folder == null: save to vault root - // folder == "./" save to same folder as current file - // folder == "folder" save to specific folder in vault - // folder == "./folder" save to specific subfolder of current active folder - if(folder && folder.startsWith("./")) { // folder relative to current file - const activeFileFolder = splitFolderAndFilename(activeView.file.path).folderpath + "/"; - folder = normalizePath(activeFileFolder + folder.substring(2)); - } - if(!folder) folder = ""; - await checkAndCreateFolder(this.app.vault,folder); const filename = activeView.file.basename + "_" + window.moment().format(this.settings.drawingFilenameDateTime) + (this.settings.compatibilityMode ? '.excalidraw' : '.excalidraw.md'); - this.embedDrawing(normalizePath(folder + "/" + filename)); - this.createDrawing(filename, inNewPane,folder==""?null:folder); + const [folder, filepath] = await getAttachmentsFolderAndFilePath(this.app,activeView.file.path,filename); + this.embedDrawing(filepath); + this.createDrawing(filename, inNewPane, folder===""?null:folder); } this.addCommand({