From caebd71dc8ab815926616e2adca4da832bf4537b Mon Sep 17 00:00:00 2001 From: Zsolt Viczian Date: Mon, 24 May 2021 15:17:31 +0200 Subject: [PATCH] 1.1.5 --- README.md | 28 ++++++----- docs/Examples/dataviewjs_familytree.md | 2 +- docs/Examples/dataviewjs_mindmap.md | 2 +- docs/Examples/insert_new_drawing.md | 2 +- docs/Examples/templater_mindmap.md | 2 +- manifest.json | 2 +- src/ExcalidrawTemplate.ts | 28 ++++++++--- src/ExcalidrawView.ts | 32 ++++++++---- src/main.ts | 69 ++++++++++++-------------- src/settings.ts | 67 ++++++++++++++++++++++--- tsconfig.json | 2 +- versions.json | 2 +- 12 files changed, 158 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 461150a..d8f0f4b 100644 --- a/README.md +++ b/README.md @@ -2,21 +2,13 @@ The Obsidian-Excalidraw plugin integrates [Excalidraw](https://excalidraw.com/), ![image](https://user-images.githubusercontent.com/14358394/115983515-d06c2c80-a5a1-11eb-8d12-c7df91d18107.png) -# Important notice to the 1.1.x update! +## Important notice to the 1.1.x update! Thank you for updating to Excalidraw 1.1.x! I have improved how drawings are embedded! You no longer need an Excalidraw codeblock. You can now embed drawings just like any other images: `![[my drawing.excalidraw]]` or `![[my drawing.excalidraw|500|left]]` or `![[my drawing.excalidraw|right-wrap]]`, `![alttext|500|right](drawing.excalidraw)`, `![](folder/drawing.excalidraw)` etc. You get the idea. -ALT+Enter and CTRL+ALT+Enter on the filename in edit mode will open up the Excalidraw editor. Click and CTRL+Click on the image in preview mode will also bring up the Excalidraw editor as expected. - -I have also added two new Command Palette commands. Both create a new drawing and immediately embed it in the document you are editing, one will open the drawing in a new workspace pane, the other within the currently active pane. - -### MIGRATION -I have added a Migration command to the Command Palette. When you select this, the program will run a search and replace for all the excalidraw codeblocks in your vault and will convert them to the new format. - -### [Ozan's Image in Editor Plugin](https://github.com/ozntel/oz-image-in-editor-obsidian) -In a nice collaboration with Ozan, his Image in Editor plugin now supports Excalidraw. I recommend installing his plugin to display drawings also in Edit mode. +### Detailed release notes are under the How to videos. # Key features - The plugin saves drawings to your vault as a file with the *.excalidraw* file extension. @@ -36,8 +28,6 @@ In a nice collaboration with Ozan, his Image in Editor plugin now supports Excal - Includes full [Templater](https://silentvoid13.github.io/Templater/) and [Dataview](https://blacksmithgu.github.io/obsidian-dataview/docs/api/intro/) support through ExcalidrawAutomate. Read detailed help + examples: [here](https://zsviczian.github.io/obsidian-excalidraw-plugin/) - REQUIRES AN OBSIDIAN SYNC SUBSCRIPTION: Temporary hack/workaround to enable Obsidian Sync for Excalidraw files. This enables almost real-time two-way sync for Excalidraw files between your devices. You can draw on your iPad with your pencil, on your Android with your stylus, and the image will be available in Obsidian on your desktop as well and vice versa. -### Please find release notes for new releases below the How-to videos. - # How to? Part 1: Intro to Obsidian-Excalidraw - Start a new drawing (3:12) @@ -65,6 +55,20 @@ Part 6: Intro to Obsidian-Excalidraw: Embedding drawings (2:08) # Release Notes +## 1.1.5 +- The template will now restore stroke properties. This means you can set up defaults in your template for stroke color, stroke width, opacity, font family, font size, fill style, stroke style, etc. This also applies to ExcalidrawAutomate. +- Added settings to customize the autogenerated filename +- Minor fixes for occasional console.log errors. + +## 1.1.0 +- ALT+Enter and CTRL+ALT+Enter on the filename in edit mode will open up the Excalidraw editor. Click and CTRL+Click on the image in preview mode will also bring up the Excalidraw editor as expected. +- I have also added two new Command Palette commands. Both create a new drawing and immediately embed it in the document you are editing, one will open the drawing in a new workspace pane, the other within the currently active pane. +- [Ozan's Image in Editor Plugin](https://github.com/ozntel/oz-image-in-editor-obsidian) +In a nice collaboration with Ozan, his Image in Editor plugin now supports Excalidraw. I recommend installing his plugin to display drawings also in Edit mode. + +### MIGRATION to 1.1.0 +I have added a Migration command to the Command Palette. When you select this, the program will run a search and replace for all the excalidraw codeblocks in your vault and will convert them to the new format. + ## 1.0.12 Freehand drawing - now includes the new freehand drawing features from Excalidraw.com - If you use Obsydian sync with Excalidraw sync, be sure to update all your devices to the new version, as the old excalidraw will simply delete the freehand drawn images and/or simply not show the drawing. diff --git a/docs/Examples/dataviewjs_familytree.md b/docs/Examples/dataviewjs_familytree.md index 0356721..0a60689 100644 --- a/docs/Examples/dataviewjs_familytree.md +++ b/docs/Examples/dataviewjs_familytree.md @@ -43,7 +43,7 @@ function buildMindmap(subtasks, depth, offset, parentObjectID) { for (let i = 0; i < subtasks.length; i++) { task = subtasks[i] - if (depth == 1) ea.style.strokeColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16); + if (depth == 1) ea.style.strokeColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,"0"); task["objectID"] = ea.addText((task.size/2+offset)*width,depth*height,task.text,{box:true}) ea.connectObjects(parentObjectID,"top",task.objectID,"bottom",{startArrowHead: 'arrow', endArrowHead: 'dot'}); if (i >= 1) { diff --git a/docs/Examples/dataviewjs_mindmap.md b/docs/Examples/dataviewjs_mindmap.md index 0ee596f..370aa27 100644 --- a/docs/Examples/dataviewjs_mindmap.md +++ b/docs/Examples/dataviewjs_mindmap.md @@ -41,7 +41,7 @@ ea.reset(); function buildMindmap(subtasks, depth, offset, parentObjectID) { if (subtasks.length == 0) return; for (let task of subtasks) { - if (depth == 1) ea.style.strokeColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16); + if (depth == 1) ea.style.strokeColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,"0"); task["objectID"] = ea.addText(depth*width,(task.size/2+offset)*height,task.text,{box:true}) ea.connectObjects(parentObjectID,"right",task.objectID,"left",{startArrowHead: 'dot'}); buildMindmap(task.subtasks, depth+1,offset,task.objectID); diff --git a/docs/Examples/insert_new_drawing.md b/docs/Examples/insert_new_drawing.md index 751e093..bbffb50 100644 --- a/docs/Examples/insert_new_drawing.md +++ b/docs/Examples/insert_new_drawing.md @@ -9,7 +9,7 @@ This [Templater](https://github.com/SilentVoid13/Templater) template will prompt const title = await tp.system.prompt("Title of the drawing?", defaultTitle); const folder = tp.file.folder(true); const transcludePath = (folder== '/' ? '' : folder + '/') + title + '.excalidraw'; - tR = String.fromCharCode(96,96,96)+'excalidraw\n[['+transcludePath+']]\n'+String.fromCharCode(96,96,96); + tR = '![['+transcludePath+']]'; const ea = ExcalidrawAutomate; ea.reset(); ea.setTheme(1); //set Theme to dark diff --git a/docs/Examples/templater_mindmap.md b/docs/Examples/templater_mindmap.md index 528eeaf..1b6849e 100644 --- a/docs/Examples/templater_mindmap.md +++ b/docs/Examples/templater_mindmap.md @@ -81,7 +81,7 @@ offsets = [0]; for(i=0;i<=linecount;i++) { depth = tree[i][IDX.depth]; - if (depth == 1) ea.style.strokeColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16); + if (depth == 1) ea.style.strokeColor = '#'+(Math.random()*0xFFFFFF<<0).toString(16).padStart(6,"0"); tree[i][IDX.objectId] = ea.addText(depth*width,((tree[i][IDX.size]/2)+offsets[depth])*height,tree[i][IDX.text],{box:true}); //set child offset equal to parent offset if((depth+1)>offsets.length) offsets.push(offsets[depth]); diff --git a/manifest.json b/manifest.json index f0f99f6..dc41ea9 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-excalidraw-plugin", "name": "Excalidraw", - "version": "1.1.4", + "version": "1.1.5", "minAppVersion": "0.11.13", "description": "An Obsidian plugin to edit and view Excalidraw drawings", "author": "Zsolt Viczian", diff --git a/src/ExcalidrawTemplate.ts b/src/ExcalidrawTemplate.ts index 9795548..236d01b 100644 --- a/src/ExcalidrawTemplate.ts +++ b/src/ExcalidrawTemplate.ts @@ -174,13 +174,27 @@ export function initExcalidrawAutomate(plugin: ExcalidrawPlugin) { params?.onNewPane ? params.onNewPane : false, params?.foldername ? params.foldername : this.plugin.settings.folder, JSON.stringify({ - "type": "excalidraw", - "version": 2, - "source": "https://excalidraw.com", - "elements": elements, - "appState": { - "theme": template ? template.appState.theme : this.canvas.theme, - "viewBackgroundColor": template? template.appState.viewBackgroundColor : this.canvas.viewBackgroundColor + type: "excalidraw", + version: 2, + source: "https://excalidraw.com", + elements: elements, + appState: { + theme: template ? template.appState.theme : this.canvas.theme, + viewBackgroundColor: template? template.appState.viewBackgroundColor : this.canvas.viewBackgroundColor, + currentItemStrokeColor: template? template.appState.currentItemStrokeColor : this.style.strokeColor, + currentItemBackgroundColor: template? template.appState.currentItemBackgroundColor : this.style.backgroundColor, + currentItemFillStyle: template? template.appState.currentItemFillStyle : this.style.fillStyle, + currentItemStrokeWidth: template? template.appState.currentItemStrokeWidth : this.style.strokeWidth, + currentItemStrokeStyle: template? template.appState.currentItemStrokeStyle : this.style.strokeStyle, + currentItemRoughness: template? template.appState.currentItemRoughness : this.style.roughness, + currentItemOpacity: template? template.appState.currentItemOpacity : this.style.opacity, + currentItemFontFamily: template? template.appState.currentItemFontFamily : this.style.fontFamily, + currentItemFontSize: template? template.appState.currentItemFontSize : this.style.fontSize, + currentItemTextAlign: template? template.appState.currentItemTextAlign : this.style.textAlign, + currentItemStrokeSharpness: template? template.appState.currentItemStrokeSharpness : this.style.strokeSharpness, + currentItemStartArrowhead: template? template.appState.currentItemStartArrowhead: this.style.startArrowHead, + currentItemEndArrowhead: template? template.appState.currentItemEndArrowhead : this.style.endArrowHead, + currentItemLinearStrokeSharpness: template? template.appState.currentItemLinearStrokeSharpness : this.style.strokeSharpness, } }) ); diff --git a/src/ExcalidrawView.ts b/src/ExcalidrawView.ts index 614348e..815ad16 100644 --- a/src/ExcalidrawView.ts +++ b/src/ExcalidrawView.ts @@ -159,12 +159,12 @@ export default class ExcalidrawView extends TextFileView { // clear the view content clear() { - if(this.excalidrawRef) { + /*if(this.excalidrawRef) { this.excalidrawRef = null; this.getScene = null; this.refresh = null; ReactDOM.unmountComponentAtNode(this.contentEl); - } + }*/ } private async loadDrawing (data:string, clear:boolean) { @@ -250,13 +250,27 @@ export default class ExcalidrawView extends TextFileView { const el: ExcalidrawElement[] = excalidrawRef.current.getSceneElements(); const st: AppState = excalidrawRef.current.getAppState(); return JSON.stringify({ - "type": "excalidraw", - "version": 2, - "source": "https://excalidraw.com", - "elements": el, - "appState": { - "theme": st.theme, - "viewBackgroundColor": st.viewBackgroundColor, + type: "excalidraw", + version: 2, + source: "https://excalidraw.com", + elements: el, + appState: { + theme: st.theme, + viewBackgroundColor: st.viewBackgroundColor, + currentItemStrokeColor: st.currentItemStrokeColor, + currentItemBackgroundColor: st.currentItemBackgroundColor, + currentItemFillStyle: st.currentItemFillStyle, + currentItemStrokeWidth: st.currentItemStrokeWidth, + currentItemStrokeStyle: st.currentItemStrokeStyle, + currentItemRoughness: st.currentItemRoughness, + currentItemOpacity: st.currentItemOpacity, + currentItemFontFamily: st.currentItemFontFamily, + currentItemFontSize: st.currentItemFontSize, + currentItemTextAlign: st.currentItemTextAlign, + currentItemStrokeSharpness: st.currentItemStrokeSharpness, + currentItemStartArrowhead: st.currentItemStartArrowhead, + currentItemEndArrowhead: st.currentItemEndArrowhead, + currentItemLinearStrokeSharpness: st.currentItemLinearStrokeSharpness, } }); }; diff --git a/src/main.ts b/src/main.ts index ff6b39a..1887b2f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -14,7 +14,7 @@ import { TAbstractFile, Notice, Tasks, -} from 'obsidian'; +} from "obsidian"; import { BLANK_DRAWING, @@ -32,21 +32,21 @@ import { RERENDER_EVENT, VIRGIL_FONT, CASCADIA_FONT -} from './constants'; -import ExcalidrawView, {ExportSettings} from './ExcalidrawView'; +} from "./constants"; +import ExcalidrawView, {ExportSettings} from "./ExcalidrawView"; import { ExcalidrawSettings, DEFAULT_SETTINGS, ExcalidrawSettingTab -} from './settings'; +} from "./settings"; import { openDialogAction, OpenFileDialog -} from './openDrawing'; +} from "./openDrawing"; import { initExcalidrawAutomate, destroyExcalidrawAutomate -} from './ExcalidrawTemplate'; +} from "./ExcalidrawTemplate"; export interface ExcalidrawAutomate extends Window { ExcalidrawAutomate: { @@ -106,7 +106,7 @@ export default class ExcalidrawPlugin extends Plugin { if (this.app.workspace.layoutReady) { this.addEventListeners(this); } else { - this.registerEvent(this.app.workspace.on('layout-ready', async () => this.addEventListeners(this))); + this.registerEvent(this.app.workspace.on("layout-ready", async () => this.addEventListeners(this))); } } @@ -398,19 +398,6 @@ export default class ExcalidrawPlugin extends Plugin { private async addEventListeners(plugin: ExcalidrawPlugin) { - const notice = new Notice( - "Welcome to Excalidraw 1.1!\n\n"+ - "This is a temporary notice. I will remove this in a week.\n\n"+ - "I have improved embedding of drawings. "+ - "You no longer need a code block, simply embed Excalidraw like any other image: " + - "![[my drawing.excalidraw]] or ![[my drawing.excalidraw|500|left]] or "+ - "![[my drawing.excalidraw|right-wrap]] or ![alt-text|500](my drawing.excalidraw) etc.\n\n"+ - "ALT+Enter and CTRL+ALT+Enter on the filename will open the drawing in the Excalidraw editor. "+ - "Click and CTRL+Click on the image in preview mode will also open the editor as expected.\n\n"+ - "MIGRATION\n"+ - 'See "Migrate" in Command Palette. Selecting this will convert all the '+ - "excalidraw code blocks in your vault to the new embed format.", 60000); - const closeDrawing = async (filePath:string) => { const leaves = plugin.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW); for (let i=0;i { if(plugin.activeExcalidrawView) { plugin.activeExcalidrawView.save(); - plugin.triggerEmbedUpdates(plugin.activeExcalidrawView.file.path); + plugin.triggerEmbedUpdates(plugin.activeExcalidrawView.file?.path); } plugin.activeExcalidrawView = (leaf.view.getViewType() == VIEW_TYPE_EXCALIDRAW) ? leaf.view as ExcalidrawView : null; if(plugin.activeExcalidrawView) - plugin.lastActiveExcalidrawFilePath = plugin.activeExcalidrawView.file.path; + plugin.lastActiveExcalidrawFilePath = plugin.activeExcalidrawView.file?.path; }; - plugin.app.workspace.on('active-leaf-change',activeLeafChangeEventHandler); + plugin.app.workspace.on("active-leaf-change",activeLeafChangeEventHandler); this.workspaceEventHandlers.set("active-leaf-change",activeLeafChangeEventHandler); } @@ -650,7 +637,7 @@ export default class ExcalidrawPlugin extends Plugin { const e = document.createEvent("Event") e.initEvent(RERENDER_EVENT,true,false); document - .querySelectorAll("div[class^='excalidraw-svg']"+ (filepath ? "[src='"+filepath+"']" : "")) + .querySelectorAll("div[class^='excalidraw-svg']"+ (filepath ? "[src='"+filepath.replaceAll("'","\\'")+"']" : "")) .forEach((el) => el.dispatchEvent(e)); } @@ -680,25 +667,33 @@ export default class ExcalidrawPlugin extends Plugin { } private getNextDefaultFilename():string { - return 'Drawing ' + window.moment().format('YYYY-MM-DD HH.mm.ss')+'.'+EXCALIDRAW_FILE_EXTENSION; + return this.settings.drawingFilenamePrefix + window.moment().format(this.settings.drawingFilenameDateTime)+'.'+EXCALIDRAW_FILE_EXTENSION; } public async createDrawing(filename: string, onNewPane: boolean, foldername?: string, initData?:string) { const folderpath = normalizePath(foldername ? foldername: this.settings.folder); - const fname = folderpath +'/'+ filename; + let fname = folderpath +'/'+ filename; const folder = this.app.vault.getAbstractFileByPath(folderpath); if (!(folder && folder instanceof TFolder)) { await this.app.vault.createFolder(folderpath); } + let file:TAbstractFile = this.app.vault.getAbstractFileByPath(fname); + let i = 0; + while(file) { + fname = folderpath + '/' + filename.slice(0,filename.lastIndexOf("."))+"_"+i+filename.slice(filename.lastIndexOf(".")); + i++; + file = this.app.vault.getAbstractFileByPath(fname); + } + if(initData) { this.openDrawing(await this.app.vault.create(fname,initData),onNewPane); return; } - const file = this.app.vault.getAbstractFileByPath(normalizePath(this.settings.templateFilePath)); - if(file && file instanceof TFile) { - const content = await this.app.vault.read(file); + const template = this.app.vault.getAbstractFileByPath(normalizePath(this.settings.templateFilePath)); + if(template && template instanceof TFile) { + const content = await this.app.vault.read(template); this.openDrawing(await this.app.vault.create(fname,content==''?BLANK_DRAWING:content), onNewPane); } else { this.openDrawing(await this.app.vault.create(fname,BLANK_DRAWING), onNewPane); diff --git a/src/settings.ts b/src/settings.ts index c39f533..cb37654 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -4,11 +4,14 @@ import { PluginSettingTab, Setting } from 'obsidian'; +import { EXCALIDRAW_FILE_EXTENSION } from './constants'; import type ExcalidrawPlugin from "./main"; export interface ExcalidrawSettings { folder: string, templateFilePath: string, + drawingFilenamePrefix: string, + drawingFilenameDateTime: string, width: string, exportWithTheme: boolean, exportWithBackground: boolean, @@ -25,6 +28,8 @@ export interface ExcalidrawSettings { export const DEFAULT_SETTINGS: ExcalidrawSettings = { folder: 'Excalidraw', templateFilePath: 'Excalidraw/Template.excalidraw', + drawingFilenamePrefix: 'Drawing ', + drawingFilenameDateTime: 'YYYY-MM-DD HH.mm.ss', width: '400', exportWithTheme: true, exportWithBackground: true, @@ -50,6 +55,20 @@ export class ExcalidrawSettingTab extends PluginSettingTab { let {containerEl} = this; this.containerEl.empty(); + new Setting(containerEl) + .setName('Default width of embedded (transcluded) image') + .setDesc('The default width of an embedded drawing. You can specify a different ' + + 'width when embedding an image using the ![[drawing.excalidraw|100]] or ' + + '[[drawing.excalidraw|100x100]] format.') + .addText(text => text + .setPlaceholder('400') + .setValue(this.plugin.settings.width) + .onChange(async (value) => { + this.plugin.settings.width = value; + await this.plugin.saveSettings(); + this.plugin.triggerEmbedUpdates(); + })); + new Setting(containerEl) .setName('Excalidraw folder') .setDesc('Default location for your Excalidraw drawings. Leaving this empty means drawings will be created in the Vault root.') @@ -74,19 +93,51 @@ export class ExcalidrawSettingTab extends PluginSettingTab { await this.plugin.saveSettings(); })); + this.containerEl.createEl('h1', {text: 'New darwing filename'}); + containerEl.createDiv('',(el) => { + el.innerHTML = '

The automatically generated filename consists of a prefix and a date. ' + + 'e.g."Drawing 2021-05-24 12.58.07".

'+ + '

Click this link for the '+ + 'date and time format reference.

'; + + }); + + const getFilenameSample = () => { + return 'The current file format is: ' + + this.plugin.settings.drawingFilenamePrefix + + window.moment().format(this.plugin.settings.drawingFilenameDateTime) + + '.' + EXCALIDRAW_FILE_EXTENSION + ''; + }; + + const filenameEl = containerEl.createEl('p',{text: ''}); + filenameEl.innerHTML = getFilenameSample(); + new Setting(containerEl) - .setName('Default width of embedded (transcluded) image') - .setDesc('The default width of an embedded drawing. You can specify a different ' + - 'width when embedding an image using the [[drawing.excalidraw|100]] or ' + - '[[drawing.excalidraw|100x100]] format.') + .setName('Filename prefix') + .setDesc('The first part of the filename') .addText(text => text - .setPlaceholder('400') - .setValue(this.plugin.settings.width) + .setPlaceholder('Drawing ') + .setValue(this.plugin.settings.drawingFilenamePrefix) .onChange(async (value) => { - this.plugin.settings.width = value; + this.plugin.settings.drawingFilenamePrefix = value.replaceAll(/[<>:"/\\|?*]/g,'_'); + text.setValue(this.plugin.settings.drawingFilenamePrefix); + filenameEl.innerHTML = getFilenameSample(); await this.plugin.saveSettings(); - this.plugin.triggerEmbedUpdates(); })); + + new Setting(containerEl) + .setName('Filename date') + .setDesc('The second part of the filename') + .addText(text => text + .setPlaceholder('YYYY-MM-DD HH.mm.ss') + .setValue(this.plugin.settings.drawingFilenameDateTime) + .onChange(async (value) => { + this.plugin.settings.drawingFilenameDateTime = value.replaceAll(/[<>:"/\\|?*]/g,'_'); + text.setValue(this.plugin.settings.drawingFilenameDateTime); + filenameEl.innerHTML = getFilenameSample(); + await this.plugin.saveSettings(); + })); + this.containerEl.createEl('h1', {text: 'Embedded image settings'}); diff --git a/tsconfig.json b/tsconfig.json index 24e04a9..632984f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,9 +11,9 @@ "importHelpers": true, "lib": [ "dom", - "es5", "scripthost", "es2020", + "esnext", "DOM.Iterable" ], "jsx": "react", diff --git a/versions.json b/versions.json index 3121ec4..ae2a50e 100644 --- a/versions.json +++ b/versions.json @@ -1,3 +1,3 @@ { - "1.1.4": "0.11.13" + "1.1.5": "0.11.13" }