mirror of
https://github.com/zsviczian/obsidian-excalidraw-plugin.git
synced 2025-08-06 05:46:28 +00:00
save image to vault
This commit is contained in:
80
package.json
80
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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FileId,string> = null; //fileId, path
|
||||
public files:Map<string,string> = 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<string,{raw:string, parsed:string}>();
|
||||
this.files = new Map<FileId,string>();
|
||||
this.files = new Map<string,string>();
|
||||
|
||||
//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<boolean> {
|
||||
//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<boolean> {
|
||||
//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();
|
||||
}
|
||||
|
||||
58
src/Utils.ts
58
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)];
|
||||
}
|
||||
21
src/main.ts
21
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({
|
||||
|
||||
Reference in New Issue
Block a user